blob: 7ed8e1653d3cbffaf85531f7fd32b924ba8dd7ac [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -07002#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -07003
Ed Tanouse3cb5a32018-08-08 14:16:49 -07004#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07005#include <boost/algorithm/string.hpp>
6#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -07007#include <dbus_singleton.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07008#include <experimental/filesystem>
9#include <fstream>
Ed Tanous911ac312017-08-15 09:37:42 -070010
Ed Tanous1abe55e2018-09-05 08:30:59 -070011namespace crow
12{
13namespace openbmc_mapper
14{
Ed Tanousba9f9a62017-10-11 16:40:35 -070015
Ed Tanouse3cb5a32018-08-08 14:16:49 -070016void introspectObjects(const std::string &processName,
17 const std::string &objectPath,
18 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070019{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070020 if (transaction->res.jsonValue.is_null())
21 {
22 transaction->res.jsonValue = {{"status", "ok"},
23 {"bus_name", processName},
24 {"objects", nlohmann::json::array()}};
25 }
26
Ed Tanous1abe55e2018-09-05 08:30:59 -070027 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070028 [transaction, processName{std::string(processName)},
29 objectPath{std::string(objectPath)}](
30 const boost::system::error_code ec,
31 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070032 if (ec)
33 {
34 BMCWEB_LOG_ERROR
35 << "Introspect call failed with error: " << ec.message()
36 << " on process: " << processName << " path: " << objectPath
37 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070038 return;
39 }
40 transaction->res.jsonValue["objects"].push_back(
41 {{"path", objectPath}});
42
43 tinyxml2::XMLDocument doc;
44
45 doc.Parse(introspect_xml.c_str());
46 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
47 if (pRoot == nullptr)
48 {
49 BMCWEB_LOG_ERROR << "XML document failed to parse "
50 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070051 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070052 else
53 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070054 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
55 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070057 const char *childPath = node->Attribute("name");
58 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 std::string newpath;
61 if (objectPath != "/")
62 {
63 newpath += objectPath;
64 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070065 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -070067 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070069
70 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 }
72 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -070074 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070076}
Ed Tanous64530012018-02-06 17:08:16 -080077
Ed Tanousaa2e59c2018-04-12 12:17:20 -070078// A smattering of common types to unpack. TODO(ed) this should really iterate
79// the sdbusplus object directly and build the json response
80using DbusRestVariantType = sdbusplus::message::variant<
81 std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
82 int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
83 bool>;
84
85using ManagedObjectType = std::vector<std::pair<
86 sdbusplus::message::object_path,
87 boost::container::flat_map<
88 std::string,
89 boost::container::flat_map<std::string, DbusRestVariantType>>>>;
90
Ed Tanous1abe55e2018-09-05 08:30:59 -070091void getManagedObjectsForEnumerate(const std::string &object_name,
Ed Tanouse3cb5a32018-08-08 14:16:49 -070092 const std::string &object_manager_path,
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 const std::string &connection_name,
94 crow::Response &res,
95 std::shared_ptr<nlohmann::json> transaction)
96{
97 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070098 [&res, transaction, object_name{std::string(object_name)}](
99 const boost::system::error_code ec,
100 const ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 if (ec)
102 {
103 BMCWEB_LOG_ERROR << ec;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700104 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 else
106 {
107 nlohmann::json &dataJson = *transaction;
Ed Tanous64530012018-02-06 17:08:16 -0800108
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 for (auto &objectPath : objects)
110 {
111 BMCWEB_LOG_DEBUG
112 << "Reading object "
113 << static_cast<const std::string &>(objectPath.first);
114 nlohmann::json &objectJson =
115 dataJson[static_cast<const std::string &>(
116 objectPath.first)];
117 if (objectJson.is_null())
118 {
119 objectJson = nlohmann::json::object();
120 }
121 for (const auto &interface : objectPath.second)
122 {
123 for (const auto &property : interface.second)
124 {
125 nlohmann::json &propertyJson =
126 objectJson[property.first];
127 mapbox::util::apply_visitor(
128 [&propertyJson](auto &&val) {
129 propertyJson = val;
130 },
131 property.second);
132 }
133 }
134 }
135 }
136
137 if (transaction.use_count() == 1)
138 {
139 res.jsonValue = {{"message", "200 OK"},
140 {"status", "ok"},
141 {"data", std::move(*transaction)}};
142 res.end();
143 }
144 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700145 connection_name, object_manager_path,
146 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
147}
148
149void findObjectManagerPathForEnumerate(
150 const std::string &object_name, const std::string &connection_name,
151 crow::Response &res, std::shared_ptr<nlohmann::json> transaction)
152{
153 crow::connections::systemBus->async_method_call(
154 [&res, transaction, object_name{std::string(object_name)},
155 connection_name{std::string(connection_name)}](
156 const boost::system::error_code ec,
157 const boost::container::flat_map<
158 std::string, boost::container::flat_map<
159 std::string, std::vector<std::string>>>
160 &objects) {
161 if (ec)
162 {
163 BMCWEB_LOG_ERROR << ec;
164 return;
165 }
166
167 for (auto pathGroup : objects)
168 {
169 for (auto connectionGroup : pathGroup.second)
170 {
171 if (connectionGroup.first == connection_name)
172 {
173 // Found the object manager path for this resource.
174 getManagedObjectsForEnumerate(
175 object_name, pathGroup.first, connection_name, res,
176 transaction);
177 return;
178 }
179 }
180 }
181 },
182 "xyz.openbmc_project.ObjectMapper",
183 "/xyz/openbmc_project/object_mapper",
184 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
185 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700186}
Ed Tanous64530012018-02-06 17:08:16 -0800187
188using GetSubTreeType = std::vector<
189 std::pair<std::string,
190 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
191
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700192// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193struct InProgressActionData
194{
195 InProgressActionData(crow::Response &res) : res(res){};
196 ~InProgressActionData()
197 {
198 if (res.result() == boost::beast::http::status::internal_server_error)
199 {
200 // Reset the json object to clear out any data that made it in
201 // before the error happened todo(ed) handle error condition with
202 // proper code
203 res.jsonValue = nlohmann::json::object();
204 }
205 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700206 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700207
Ed Tanous1abe55e2018-09-05 08:30:59 -0700208 void setErrorStatus()
209 {
210 res.result(boost::beast::http::status::internal_server_error);
211 }
212 crow::Response &res;
213 std::string path;
214 std::string methodName;
215 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700216};
217
Ed Tanous1abe55e2018-09-05 08:30:59 -0700218std::vector<std::string> dbusArgSplit(const std::string &string)
219{
220 std::vector<std::string> ret;
221 if (string.empty())
222 {
223 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700224 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 ret.push_back("");
226 int containerDepth = 0;
227
228 for (std::string::const_iterator character = string.begin();
229 character != string.end(); character++)
230 {
231 ret.back() += *character;
232 switch (*character)
233 {
234 case ('a'):
235 break;
236 case ('('):
237 case ('{'):
238 containerDepth++;
239 break;
240 case ('}'):
241 case (')'):
242 containerDepth--;
243 if (containerDepth == 0)
244 {
245 if (character + 1 != string.end())
246 {
247 ret.push_back("");
248 }
249 }
250 break;
251 default:
252 if (containerDepth == 0)
253 {
254 if (character + 1 != string.end())
255 {
256 ret.push_back("");
257 }
258 }
259 break;
260 }
261 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700262}
263
Ed Tanousd76323e2018-08-07 14:35:40 -0700264int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 const nlohmann::json &input_json)
266{
267 int r = 0;
268 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
269 << " to type: " << arg_type;
270 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700271
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 // Assume a single object for now.
273 const nlohmann::json *j = &input_json;
274 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700275
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700276 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700277 {
278 // If we are decoding multiple objects, grab the pointer to the
279 // iterator, and increment it for the next loop
280 if (argTypes.size() > 1)
281 {
282 if (jIt == input_json.end())
283 {
284 return -2;
285 }
286 j = &*jIt;
287 jIt++;
288 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 const int64_t *intValue = j->get_ptr<const int64_t *>();
290 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
291 const std::string *stringValue = j->get_ptr<const std::string *>();
292 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700293 const bool *b = j->get_ptr<const bool *>();
294 int64_t v = 0;
295 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700296
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 // Do some basic type conversions that make sense. uint can be
298 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700299 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 v = static_cast<int64_t>(*uintValue);
302 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700304 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700306 d = static_cast<double>(*uintValue);
307 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700309 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700311 d = static_cast<double>(*intValue);
312 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700313 }
314
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700315 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700317 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 {
319 return -1;
320 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700321 r = sd_bus_message_append_basic(m, argCode[0],
322 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323 if (r < 0)
324 {
325 return r;
326 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700327 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700328 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700330 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331 {
332 return -1;
333 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700334 int32_t i = static_cast<int32_t>(*intValue);
335 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700336 if (r < 0)
337 {
338 return r;
339 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700340 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700341 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 {
343 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700344 int boolInt = false;
345 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700347 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 }
349 else if (b != nullptr)
350 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700351 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700353 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700355 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 }
357 else
358 {
359 return -1;
360 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700361 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700362 if (r < 0)
363 {
364 return r;
365 }
366 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700367 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700369 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 {
371 return -1;
372 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700373 int16_t n = static_cast<int16_t>(*intValue);
374 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700375 if (r < 0)
376 {
377 return r;
378 }
379 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700380 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700381 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700382 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700383 {
384 return -1;
385 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700386 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700387 if (r < 0)
388 {
389 return r;
390 }
391 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700392 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700393 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700394 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700395 {
396 return -1;
397 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700398 uint8_t y = static_cast<uint8_t>(*uintValue);
399 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700401 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700403 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 {
405 return -1;
406 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700407 uint16_t q = static_cast<uint16_t>(*uintValue);
408 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700410 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700412 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
414 return -1;
415 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700416 uint32_t u = static_cast<uint32_t>(*uintValue);
417 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700419 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700421 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 {
423 return -1;
424 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700425 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700426 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700427 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700429 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700431 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700432 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700433 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700435 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700436 if (r < 0)
437 {
438 return r;
439 }
440
441 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
442 ++it)
443 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700444 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 if (r < 0)
446 {
447 return r;
448 }
449
450 it++;
451 }
452 sd_bus_message_close_container(m);
453 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700454 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700456 std::string containedType = argCode.substr(1);
457 BMCWEB_LOG_DEBUG << "variant type: " << argCode
458 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700460 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461 if (r < 0)
462 {
463 return r;
464 }
465
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700466 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 if (r < 0)
468 {
469 return r;
470 }
471
472 r = sd_bus_message_close_container(m);
473 if (r < 0)
474 {
475 return r;
476 }
477 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700478 else if (boost::starts_with(argCode, "(") &&
479 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700481 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700483 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700485 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700486 {
487 if (it == j->end())
488 {
489 return -1;
490 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700491 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700492 if (r < 0)
493 {
494 return r;
495 }
496 it++;
497 }
498 r = sd_bus_message_close_container(m);
499 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700500 else if (boost::starts_with(argCode, "{") &&
501 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700503 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700505 containedType.c_str());
506 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 if (codes.size() != 2)
508 {
509 return -1;
510 }
511 const std::string &key_type = codes[0];
512 const std::string &value_type = codes[1];
513 for (auto it : j->items())
514 {
515 r = convertJsonToDbus(m, key_type, it.key());
516 if (r < 0)
517 {
518 return r;
519 }
520
521 r = convertJsonToDbus(m, value_type, it.value());
522 if (r < 0)
523 {
524 return r;
525 }
526 }
527 r = sd_bus_message_close_container(m);
528 }
529 else
530 {
531 return -2;
532 }
533 if (r < 0)
534 {
535 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700536 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700537
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 if (argTypes.size() > 1)
539 {
540 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700541 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700542 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700543}
544
Ed Tanousd76323e2018-08-07 14:35:40 -0700545void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 const std::string &connectionName)
547{
548 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
549 << connectionName;
550 crow::connections::systemBus->async_method_call(
551 [transaction, connectionName{std::string(connectionName)}](
552 const boost::system::error_code ec,
553 const std::string &introspect_xml) {
554 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
555 if (ec)
556 {
557 BMCWEB_LOG_ERROR
558 << "Introspect call failed with error: " << ec.message()
559 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700560 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 else
562 {
563 tinyxml2::XMLDocument doc;
564
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700565 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700566 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
567 if (pRoot == nullptr)
568 {
569 BMCWEB_LOG_ERROR << "XML document failed to parse "
570 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700571 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700573 tinyxml2::XMLElement *interfaceNode =
574 pRoot->FirstChildElement("interface");
575 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700577 const char *thisInterfaceName =
578 interfaceNode->Attribute("name");
579 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 tinyxml2::XMLElement *methodNode =
582 interfaceNode->FirstChildElement("method");
583 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700585 const char *thisMethodName =
586 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 << thisMethodName;
589 if (thisMethodName != nullptr &&
590 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 BMCWEB_LOG_DEBUG
593 << "Found method named " << thisMethodName
594 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 sdbusplus::message::message m =
596 crow::connections::systemBus
597 ->new_method_call(
598 connectionName.c_str(),
599 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 transaction->methodName.c_str());
602
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700603 tinyxml2::XMLElement *argumentNode =
604 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 transaction->arguments.begin();
608
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700611 const char *argDirection =
612 argumentNode->Attribute("direction");
613 const char *argType =
614 argumentNode->Attribute("type");
615 if (argDirection != nullptr &&
616 argType != nullptr &&
617 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700619
620 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 transaction->arguments.end())
622 {
623 transaction->setErrorStatus();
624 return;
625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 if (convertJsonToDbus(
627 m.get(), std::string(argType),
628 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 {
630 transaction->setErrorStatus();
631 return;
632 }
633
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700636 argumentNode =
637 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 crow::connections::systemBus->async_send(
641 m, [transaction](
642 boost::system::error_code ec,
643 sdbusplus::message::message &m) {
644 if (ec)
645 {
646 transaction->setErrorStatus();
647 return;
648 }
649 transaction->res.jsonValue = {
650 {"status", "ok"},
651 {"message", "200 OK"},
652 {"data", nullptr}};
653 });
654 break;
655 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700656 methodNode =
657 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 interfaceNode =
661 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 }
663 }
664 },
665 connectionName, transaction->path,
666 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700667}
668
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700669void handleAction(const crow::Request &req, crow::Response &res,
670 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
673 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 nlohmann::json requestDbusData =
675 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700676
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 if (requestDbusData.is_discarded())
678 {
679 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700680 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 return;
682 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700683 nlohmann::json::iterator data = requestDbusData.find("data");
684 if (data == requestDbusData.end())
685 {
686 res.result(boost::beast::http::status::bad_request);
687 res.end();
688 return;
689 }
690
691 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 {
693 res.result(boost::beast::http::status::bad_request);
694 res.end();
695 return;
696 }
697 auto transaction = std::make_shared<InProgressActionData>(res);
698
699 transaction->path = objectPath;
700 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700701 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 crow::connections::systemBus->async_method_call(
703 [transaction](
704 const boost::system::error_code ec,
705 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 &interfaceNames) {
707 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700709 BMCWEB_LOG_ERROR << "Can't find object";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 transaction->setErrorStatus();
711 return;
712 }
713
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700714 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
715 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716
717 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700718 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 {
720 findActionOnInterface(transaction, object.first);
721 }
722 },
723 "xyz.openbmc_project.ObjectMapper",
724 "/xyz/openbmc_project/object_mapper",
725 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
726 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700727}
728
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700729void handleList(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730{
731 crow::connections::systemBus->async_method_call(
732 [&res](const boost::system::error_code ec,
733 std::vector<std::string> &objectPaths) {
734 if (ec)
735 {
736 res.result(boost::beast::http::status::internal_server_error);
737 }
738 else
739 {
740 res.jsonValue = {{"status", "ok"},
741 {"message", "200 OK"},
742 {"data", std::move(objectPaths)}};
743 }
744 res.end();
745 },
746 "xyz.openbmc_project.ObjectMapper",
747 "/xyz/openbmc_project/object_mapper",
748 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700749 static_cast<int32_t>(0), std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700751
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700752void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753{
754 crow::connections::systemBus->async_method_call(
755 [&res, objectPath{std::string(objectPath)}](
756 const boost::system::error_code ec,
757 const GetSubTreeType &object_names) {
758 if (ec)
759 {
760 res.jsonValue = {{"message", "200 OK"},
761 {"status", "ok"},
762 {"data", nlohmann::json::object()}};
Ed Tanous64530012018-02-06 17:08:16 -0800763
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 res.end();
765 return;
766 }
Ed Tanous64530012018-02-06 17:08:16 -0800767
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 boost::container::flat_set<std::string> connections;
Ed Tanous64530012018-02-06 17:08:16 -0800769
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 for (const auto &object : object_names)
771 {
772 for (const auto &Connection : object.second)
773 {
774 connections.insert(Connection.first);
775 }
776 }
777
778 if (connections.size() <= 0)
779 {
780 res.result(boost::beast::http::status::not_found);
781 res.end();
782 return;
783 }
784 auto transaction =
785 std::make_shared<nlohmann::json>(nlohmann::json::object());
786 for (const std::string &Connection : connections)
787 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700788 findObjectManagerPathForEnumerate(objectPath, Connection, res,
789 transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 }
791 },
792 "xyz.openbmc_project.ObjectMapper",
793 "/xyz/openbmc_project/object_mapper",
794 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
795 (int32_t)0, std::array<std::string, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800796}
Ed Tanous911ac312017-08-15 09:37:42 -0700797
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700798void handleGet(crow::Response &res, std::string &objectPath,
799 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700801 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
802 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700803 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -0700804
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 std::shared_ptr<std::string> path =
806 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -0700807
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 using GetObjectType =
809 std::vector<std::pair<std::string, std::vector<std::string>>>;
810 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700811 [&res, path, propertyName](const boost::system::error_code ec,
812 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 if (ec || object_names.size() <= 0)
814 {
815 res.result(boost::beast::http::status::not_found);
816 res.end();
817 return;
818 }
819 std::shared_ptr<nlohmann::json> response =
820 std::make_shared<nlohmann::json>(nlohmann::json::object());
821 // The mapper should never give us an empty interface names list,
822 // but check anyway
823 for (const std::pair<std::string, std::vector<std::string>>
824 connection : object_names)
825 {
826 const std::vector<std::string> &interfaceNames =
827 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700828
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 if (interfaceNames.size() <= 0)
830 {
831 res.result(boost::beast::http::status::not_found);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700832 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 return;
834 }
835
836 for (const std::string &interface : interfaceNames)
837 {
838 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700839 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700840 const boost::system::error_code ec,
841 const std::vector<
842 std::pair<std::string, DbusRestVariantType>>
843 &properties) {
844 if (ec)
845 {
846 BMCWEB_LOG_ERROR << "Bad dbus request error: "
847 << ec;
848 }
849 else
850 {
851 for (const std::pair<std::string,
852 DbusRestVariantType>
853 &property : properties)
854 {
855 // if property name is empty, or matches our
856 // search query, add it to the response json
857
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700858 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700859 {
860 mapbox::util::apply_visitor(
861 [&response, &property](auto &&val) {
862 (*response)[property.first] =
863 val;
864 },
865 property.second);
866 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700867 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700868 {
869 mapbox::util::apply_visitor(
870 [&response](auto &&val) {
871 (*response) = val;
872 },
873 property.second);
874 }
875 }
876 }
877 if (response.use_count() == 1)
878 {
879 res.jsonValue = {{"status", "ok"},
880 {"message", "200 OK"},
881 {"data", *response}};
882
883 res.end();
884 }
885 },
886 connection.first, *path,
887 "org.freedesktop.DBus.Properties", "GetAll", interface);
888 }
889 }
890 },
891 "xyz.openbmc_project.ObjectMapper",
892 "/xyz/openbmc_project/object_mapper",
893 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
894 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700895}
896
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897struct AsyncPutRequest
898{
899 AsyncPutRequest(crow::Response &res) : res(res)
900 {
901 res.jsonValue = {
902 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
903 }
904 ~AsyncPutRequest()
905 {
906 if (res.result() == boost::beast::http::status::internal_server_error)
907 {
908 // Reset the json object to clear out any data that made it in
909 // before the error happened todo(ed) handle error condition with
910 // proper code
911 res.jsonValue = nlohmann::json::object();
912 }
913
914 if (res.jsonValue.empty())
915 {
916 res.result(boost::beast::http::status::forbidden);
917 res.jsonValue = {
918 {"status", "error"},
919 {"message", "403 Forbidden"},
920 {"data",
921 {{"message", "The specified property cannot be created: " +
922 propertyName}}}};
923 }
924
925 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700926 }
927
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 void setErrorStatus()
929 {
930 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700931 }
932
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933 crow::Response &res;
934 std::string objectPath;
935 std::string propertyName;
936 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700937};
938
Ed Tanousd76323e2018-08-07 14:35:40 -0700939void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940 const std::string &objectPath, const std::string &destProperty)
941{
942 nlohmann::json requestDbusData =
943 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700944
Ed Tanous1abe55e2018-09-05 08:30:59 -0700945 if (requestDbusData.is_discarded())
946 {
947 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700948 res.end();
949 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700950 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700951
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
953 if (propertyIt == requestDbusData.end())
954 {
955 res.result(boost::beast::http::status::bad_request);
956 res.end();
957 return;
958 }
959 const nlohmann::json &propertySetValue = *propertyIt;
960 auto transaction = std::make_shared<AsyncPutRequest>(res);
961 transaction->objectPath = objectPath;
962 transaction->propertyName = destProperty;
963 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -0700964
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 using GetObjectType =
966 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -0700967
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968 crow::connections::systemBus->async_method_call(
969 [transaction](const boost::system::error_code ec,
970 const GetObjectType &object_names) {
971 if (!ec && object_names.size() <= 0)
972 {
973 transaction->res.result(boost::beast::http::status::not_found);
974 return;
975 }
Ed Tanous911ac312017-08-15 09:37:42 -0700976
Ed Tanous1abe55e2018-09-05 08:30:59 -0700977 for (const std::pair<std::string, std::vector<std::string>>
978 connection : object_names)
979 {
980 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -0700981
Ed Tanous1abe55e2018-09-05 08:30:59 -0700982 crow::connections::systemBus->async_method_call(
983 [connectionName{std::string(connectionName)},
984 transaction](const boost::system::error_code ec,
985 const std::string &introspectXml) {
986 if (ec)
987 {
988 BMCWEB_LOG_ERROR
989 << "Introspect call failed with error: "
990 << ec.message()
991 << " on process: " << connectionName;
992 transaction->setErrorStatus();
993 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700994 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700995 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -0700996
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 doc.Parse(introspectXml.c_str());
998 tinyxml2::XMLNode *pRoot =
999 doc.FirstChildElement("node");
1000 if (pRoot == nullptr)
1001 {
1002 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1003 << introspectXml;
1004 transaction->setErrorStatus();
1005 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001006 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007 tinyxml2::XMLElement *ifaceNode =
1008 pRoot->FirstChildElement("interface");
1009 while (ifaceNode != nullptr)
1010 {
1011 const char *interfaceName =
1012 ifaceNode->Attribute("name");
1013 BMCWEB_LOG_DEBUG << "found interface "
1014 << interfaceName;
1015 tinyxml2::XMLElement *propNode =
1016 ifaceNode->FirstChildElement("property");
1017 while (propNode != nullptr)
1018 {
1019 const char *propertyName =
1020 propNode->Attribute("name");
1021 BMCWEB_LOG_DEBUG << "Found property "
1022 << propertyName;
1023 if (propertyName == transaction->propertyName)
1024 {
1025 const char *argType =
1026 propNode->Attribute("type");
1027 if (argType != nullptr)
1028 {
1029 sdbusplus::message::message m =
1030 crow::connections::systemBus
1031 ->new_method_call(
1032 connectionName.c_str(),
1033 transaction->objectPath
1034 .c_str(),
1035 "org.freedesktop.DBus."
1036 "Properties",
1037 "Set");
1038 m.append(interfaceName,
1039 transaction->propertyName);
1040 int r = sd_bus_message_open_container(
1041 m.get(), SD_BUS_TYPE_VARIANT,
1042 argType);
1043 if (r < 0)
1044 {
1045 transaction->setErrorStatus();
1046 return;
1047 }
1048 r = convertJsonToDbus(
1049 m.get(), argType,
1050 transaction->propertyValue);
1051 if (r < 0)
1052 {
1053 transaction->setErrorStatus();
1054 return;
1055 }
1056 r = sd_bus_message_close_container(
1057 m.get());
1058 if (r < 0)
1059 {
1060 transaction->setErrorStatus();
1061 return;
1062 }
Ed Tanous911ac312017-08-15 09:37:42 -07001063
Ed Tanous1abe55e2018-09-05 08:30:59 -07001064 crow::connections::systemBus
1065 ->async_send(
1066 m,
1067 [transaction](
1068 boost::system::error_code
1069 ec,
1070 sdbusplus::message::message
1071 &m) {
1072 BMCWEB_LOG_DEBUG << "sent";
1073 if (ec)
1074 {
1075 transaction->res
1076 .jsonValue
1077 ["status"] =
1078 "error";
1079 transaction->res
1080 .jsonValue
1081 ["message"] =
1082 ec.message();
1083 }
1084 });
1085 }
1086 }
1087 propNode =
1088 propNode->NextSiblingElement("property");
1089 }
1090 ifaceNode =
1091 ifaceNode->NextSiblingElement("interface");
1092 }
1093 },
1094 connectionName, transaction->objectPath,
1095 "org.freedesktop.DBus.Introspectable", "Introspect");
1096 }
1097 },
1098 "xyz.openbmc_project.ObjectMapper",
1099 "/xyz/openbmc_project/object_mapper",
1100 "xyz.openbmc_project.ObjectMapper", "GetObject",
1101 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001102}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001103
1104template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1105{
1106 BMCWEB_ROUTE(app, "/bus/")
1107 .methods("GET"_method)(
1108 [](const crow::Request &req, crow::Response &res) {
1109 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1110 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001111 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112 });
1113
1114 BMCWEB_ROUTE(app, "/bus/system/")
1115 .methods("GET"_method)(
1116 [](const crow::Request &req, crow::Response &res) {
1117 auto myCallback = [&res](const boost::system::error_code ec,
1118 std::vector<std::string> &names) {
1119 if (ec)
1120 {
1121 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1122 res.result(
1123 boost::beast::http::status::internal_server_error);
1124 }
1125 else
1126 {
1127 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001128 res.jsonValue = {{"status", "ok"}};
1129 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001130 for (auto &name : names)
1131 {
1132 objectsSub.push_back({{"name", name}});
1133 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001134 }
1135 res.end();
1136 };
1137 crow::connections::systemBus->async_method_call(
1138 std::move(myCallback), "org.freedesktop.DBus", "/",
1139 "org.freedesktop.DBus", "ListNames");
1140 });
1141
1142 BMCWEB_ROUTE(app, "/list/")
1143 .methods("GET"_method)(
1144 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001145 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001146 });
1147
1148 BMCWEB_ROUTE(app, "/xyz/<path>")
1149 .methods("GET"_method, "PUT"_method,
1150 "POST"_method)([](const crow::Request &req,
1151 crow::Response &res,
1152 const std::string &path) {
1153 std::string objectPath = "/xyz/" + path;
1154
1155 // Trim any trailing "/" at the end
1156 if (boost::ends_with(objectPath, "/"))
1157 {
1158 objectPath.pop_back();
1159 }
1160
1161 // If accessing a single attribute, fill in and update objectPath,
1162 // otherwise leave destProperty blank
1163 std::string destProperty = "";
1164 const char *attrSeperator = "/attr/";
1165 size_t attrPosition = path.find(attrSeperator);
1166 if (attrPosition != path.npos)
1167 {
1168 objectPath = "/xyz/" + path.substr(0, attrPosition);
1169 destProperty = path.substr(attrPosition + strlen(attrSeperator),
1170 path.length());
1171 }
1172
1173 if (req.method() == "POST"_method)
1174 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001175 constexpr const char *actionSeperator = "/action/";
1176 size_t actionPosition = path.find(actionSeperator);
1177 if (actionPosition != path.npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001178 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001179 objectPath = "/xyz/" + path.substr(0, actionPosition);
1180 std::string postProperty =
1181 path.substr((actionPosition + strlen(actionSeperator)),
1182 path.length());
1183 handleAction(req, res, objectPath, postProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001184 return;
1185 }
1186 }
1187 else if (req.method() == "GET"_method)
1188 {
1189 if (boost::ends_with(objectPath, "/enumerate"))
1190 {
1191 objectPath.erase(objectPath.end() - 10, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001192 handleEnumerate(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001193 }
1194 else if (boost::ends_with(objectPath, "/list"))
1195 {
1196 objectPath.erase(objectPath.end() - 5, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001197 handleList(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001198 }
1199 else
1200 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001201 handleGet(res, objectPath, destProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001202 }
1203 return;
1204 }
1205 else if (req.method() == "PUT"_method)
1206 {
1207 handlePut(req, res, objectPath, destProperty);
1208 return;
1209 }
1210
1211 res.result(boost::beast::http::status::method_not_allowed);
1212 res.end();
1213 });
1214
Ed Tanous1abe55e2018-09-05 08:30:59 -07001215 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1216 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1217 const std::string &dumpId) {
1218 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1219 if (!std::regex_match(dumpId, validFilename))
1220 {
1221 res.result(boost::beast::http::status::not_found);
1222 res.end();
1223 return;
1224 }
1225 std::experimental::filesystem::path loc(
1226 "/var/lib/phosphor-debug-collector/dumps");
1227
1228 loc += dumpId;
1229
1230 if (!std::experimental::filesystem::exists(loc) ||
1231 !std::experimental::filesystem::is_directory(loc))
1232 {
1233 res.result(boost::beast::http::status::not_found);
1234 res.end();
1235 return;
1236 }
1237 std::experimental::filesystem::directory_iterator files(loc);
1238 for (auto &file : files)
1239 {
1240 std::ifstream readFile(file.path());
1241 if (readFile.good())
1242 {
1243 continue;
1244 }
1245 res.addHeader("Content-Type", "application/octet-stream");
1246 res.body() = {std::istreambuf_iterator<char>(readFile),
1247 std::istreambuf_iterator<char>()};
1248 res.end();
1249 }
1250 res.result(boost::beast::http::status::not_found);
1251 res.end();
1252 return;
1253 });
1254
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001255 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001256 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001257 const std::string &Connection) {
1258 introspectObjects(Connection, "/",
1259 std::make_shared<bmcweb::AsyncResp>(res));
1260 });
1261
1262 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1263 .methods("GET"_method,
1264 "POST"_method)([](const crow::Request &req,
1265 crow::Response &res,
1266 const std::string &processName,
1267 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001268 std::vector<std::string> strs;
1269 boost::split(strs, requestedPath, boost::is_any_of("/"));
1270 std::string objectPath;
1271 std::string interfaceName;
1272 std::string methodName;
1273 auto it = strs.begin();
1274 if (it == strs.end())
1275 {
1276 objectPath = "/";
1277 }
1278 while (it != strs.end())
1279 {
1280 // Check if segment contains ".". If it does, it must be an
1281 // interface
1282 if (it->find(".") != std::string::npos)
1283 {
1284 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001285 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001286 // as part of our <path> specifier above, which causes the
1287 // normal trailing backslash redirector to fail.
1288 }
1289 else if (!it->empty())
1290 {
1291 objectPath += "/" + *it;
1292 }
1293 it++;
1294 }
1295 if (it != strs.end())
1296 {
1297 interfaceName = *it;
1298 it++;
1299
1300 // after interface, we might have a method name
1301 if (it != strs.end())
1302 {
1303 methodName = *it;
1304 it++;
1305 }
1306 }
1307 if (it != strs.end())
1308 {
1309 // if there is more levels past the method name, something went
1310 // wrong, return not found
1311 res.result(boost::beast::http::status::not_found);
1312 res.end();
1313 return;
1314 }
1315 if (interfaceName.empty())
1316 {
1317 crow::connections::systemBus->async_method_call(
1318 [&, processName,
1319 objectPath](const boost::system::error_code ec,
1320 const std::string &introspect_xml) {
1321 if (ec)
1322 {
1323 BMCWEB_LOG_ERROR
1324 << "Introspect call failed with error: "
1325 << ec.message()
1326 << " on process: " << processName
1327 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001328 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001329 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001330 tinyxml2::XMLDocument doc;
1331
1332 doc.Parse(introspect_xml.c_str());
1333 tinyxml2::XMLNode *pRoot =
1334 doc.FirstChildElement("node");
1335 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001336 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001337 BMCWEB_LOG_ERROR << "XML document failed to parse "
1338 << processName << " " << objectPath
1339 << "\n";
1340 res.jsonValue = {{"status", "XML parse error"}};
1341 res.result(boost::beast::http::status::
1342 internal_server_error);
1343 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001344 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001345
1346 BMCWEB_LOG_DEBUG << introspect_xml;
1347 res.jsonValue = {{"status", "ok"},
1348 {"bus_name", processName},
1349 {"object_path", objectPath}};
1350 nlohmann::json &interfacesArray =
1351 res.jsonValue["interfaces"];
1352 interfacesArray = nlohmann::json::array();
1353 tinyxml2::XMLElement *interface =
1354 pRoot->FirstChildElement("interface");
1355
1356 while (interface != nullptr)
1357 {
1358 const char *ifaceName =
1359 interface->Attribute("name");
1360 if (ifaceName != nullptr)
1361 {
1362 interfacesArray.push_back(
1363 {{"name", ifaceName}});
1364 }
1365
1366 interface =
1367 interface->NextSiblingElement("interface");
1368 }
1369
Ed Tanous1abe55e2018-09-05 08:30:59 -07001370 res.end();
1371 },
1372 processName, objectPath,
1373 "org.freedesktop.DBus.Introspectable", "Introspect");
1374 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001375 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001376 {
1377 crow::connections::systemBus->async_method_call(
1378 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001379 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001380 const boost::system::error_code ec,
1381 const std::string &introspect_xml) {
1382 if (ec)
1383 {
1384 BMCWEB_LOG_ERROR
1385 << "Introspect call failed with error: "
1386 << ec.message()
1387 << " on process: " << processName
1388 << " path: " << objectPath << "\n";
1389 }
1390 else
1391 {
1392 tinyxml2::XMLDocument doc;
1393
1394 doc.Parse(introspect_xml.c_str());
1395 tinyxml2::XMLNode *pRoot =
1396 doc.FirstChildElement("node");
1397 if (pRoot == nullptr)
1398 {
1399 BMCWEB_LOG_ERROR
1400 << "XML document failed to parse "
1401 << processName << " " << objectPath << "\n";
1402 res.result(boost::beast::http::status::
1403 internal_server_error);
1404 }
1405 else
1406 {
1407 tinyxml2::XMLElement *node =
1408 pRoot->FirstChildElement("node");
1409
1410 // if we know we're the only call, build the
1411 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001412 tinyxml2::XMLElement *interface =
1413 pRoot->FirstChildElement("interface");
1414
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001415 res.jsonValue = {
1416 {"status", "ok"},
1417 {"bus_name", processName},
1418 {"interface", interfaceName},
1419 {"object_path", objectPath},
1420 {"properties", nlohmann::json::object()}};
1421
1422 nlohmann::json &methodsArray =
1423 res.jsonValue["methods"];
1424 methodsArray = nlohmann::json::array();
1425
1426 nlohmann::json &signalsArray =
1427 res.jsonValue["signals"];
1428 signalsArray = nlohmann::json::array();
1429
Ed Tanous1abe55e2018-09-05 08:30:59 -07001430 while (interface != nullptr)
1431 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001432 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001433 interface->Attribute("name");
1434
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001435 if (ifaceName != nullptr &&
1436 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001437 {
1438 tinyxml2::XMLElement *methods =
1439 interface->FirstChildElement(
1440 "method");
1441 while (methods != nullptr)
1442 {
1443 nlohmann::json argsArray =
1444 nlohmann::json::array();
1445 tinyxml2::XMLElement *arg =
1446 methods->FirstChildElement(
1447 "arg");
1448 while (arg != nullptr)
1449 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001450 nlohmann::json thisArg;
1451 for (const char *fieldName :
1452 std::array<const char *,
1453 3>{"name",
1454 "direction",
1455 "type"})
1456 {
1457 const char *fieldValue =
1458 arg->Attribute(
1459 fieldName);
1460 if (fieldValue != nullptr)
1461 {
1462 thisArg[fieldName] =
1463 fieldValue;
1464 }
1465 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001466 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001467 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001468 arg = arg->NextSiblingElement(
1469 "arg");
1470 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001471
1472 const char *name =
1473 methods->Attribute("name");
1474 if (name != nullptr)
1475 {
1476 methodsArray.push_back(
1477 {{"name", name},
1478 {"uri", "/bus/system/" +
1479 processName +
1480 objectPath +
1481 "/" +
1482 interfaceName +
1483 "/" + name},
1484 {"args", argsArray}});
1485 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001486 methods =
1487 methods->NextSiblingElement(
1488 "method");
1489 }
1490 tinyxml2::XMLElement *signals =
1491 interface->FirstChildElement(
1492 "signal");
1493 while (signals != nullptr)
1494 {
1495 nlohmann::json argsArray =
1496 nlohmann::json::array();
1497
1498 tinyxml2::XMLElement *arg =
1499 signals->FirstChildElement(
1500 "arg");
1501 while (arg != nullptr)
1502 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001503 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001504 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001505 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001506 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001507 if (name != nullptr &&
1508 type != nullptr)
1509 {
1510 argsArray.push_back({
1511 {"name", name},
1512 {"type", type},
1513 });
1514 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001515 arg = arg->NextSiblingElement(
1516 "arg");
1517 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001518 const char *name =
1519 signals->Attribute("name");
1520 if (name != nullptr)
1521 {
1522 signalsArray.push_back(
1523 {{"name", name},
1524 {"args", argsArray}});
1525 }
1526
Ed Tanous1abe55e2018-09-05 08:30:59 -07001527 signals =
1528 signals->NextSiblingElement(
1529 "signal");
1530 }
1531
Ed Tanous1abe55e2018-09-05 08:30:59 -07001532 break;
1533 }
1534
1535 interface = interface->NextSiblingElement(
1536 "interface");
1537 }
1538 if (interface == nullptr)
1539 {
1540 // if we got to the end of the list and
1541 // never found a match, throw 404
1542 res.result(
1543 boost::beast::http::status::not_found);
1544 }
1545 }
1546 }
1547 res.end();
1548 },
1549 processName, objectPath,
1550 "org.freedesktop.DBus.Introspectable", "Introspect");
1551 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001552 else
1553 {
1554 if (req.method() != "POST"_method)
1555 {
1556 res.result(boost::beast::http::status::not_found);
1557 res.end();
1558 return;
1559 }
1560
1561 nlohmann::json requestDbusData =
1562 nlohmann::json::parse(req.body, nullptr, false);
1563
1564 if (requestDbusData.is_discarded())
1565 {
1566 res.result(boost::beast::http::status::bad_request);
1567 res.end();
1568 return;
1569 }
1570 if (!requestDbusData.is_array())
1571 {
1572 res.result(boost::beast::http::status::bad_request);
1573 res.end();
1574 return;
1575 }
1576 auto transaction = std::make_shared<InProgressActionData>(res);
1577
1578 transaction->path = objectPath;
1579 transaction->methodName = methodName;
1580 transaction->arguments = std::move(requestDbusData);
1581
1582 findActionOnInterface(transaction, processName);
1583 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584 });
1585}
1586} // namespace openbmc_mapper
1587} // namespace crow