blob: 1fefa405fd220ae498309b656ba5deccb7735002 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -070016#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070024#include <experimental/filesystem>
25#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070026#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
30namespace openbmc_mapper
31{
Ed Tanousba9f9a62017-10-11 16:40:35 -070032
Ed Tanouse3cb5a32018-08-08 14:16:49 -070033void introspectObjects(const std::string &processName,
34 const std::string &objectPath,
35 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070036{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070037 if (transaction->res.jsonValue.is_null())
38 {
39 transaction->res.jsonValue = {{"status", "ok"},
40 {"bus_name", processName},
41 {"objects", nlohmann::json::array()}};
42 }
43
Ed Tanous1abe55e2018-09-05 08:30:59 -070044 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070045 [transaction, processName{std::string(processName)},
46 objectPath{std::string(objectPath)}](
47 const boost::system::error_code ec,
48 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 if (ec)
50 {
51 BMCWEB_LOG_ERROR
52 << "Introspect call failed with error: " << ec.message()
53 << " on process: " << processName << " path: " << objectPath
54 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070055 return;
56 }
57 transaction->res.jsonValue["objects"].push_back(
58 {{"path", objectPath}});
59
60 tinyxml2::XMLDocument doc;
61
62 doc.Parse(introspect_xml.c_str());
63 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
64 if (pRoot == nullptr)
65 {
66 BMCWEB_LOG_ERROR << "XML document failed to parse "
67 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070068 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 else
70 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070071 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
72 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070074 const char *childPath = node->Attribute("name");
75 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070076 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 std::string newpath;
78 if (objectPath != "/")
79 {
80 newpath += objectPath;
81 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070082 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -070084 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -070085 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070086
87 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 }
89 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -070091 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070093}
Ed Tanous64530012018-02-06 17:08:16 -080094
Ed Tanous1abe55e2018-09-05 08:30:59 -070095void getManagedObjectsForEnumerate(const std::string &object_name,
Ed Tanouse3cb5a32018-08-08 14:16:49 -070096 const std::string &object_manager_path,
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 const std::string &connection_name,
98 crow::Response &res,
99 std::shared_ptr<nlohmann::json> transaction)
100{
101 crow::connections::systemBus->async_method_call(
James Feist5b4aa862018-08-16 14:07:01 -0700102 [&res, transaction](const boost::system::error_code ec,
103 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 if (ec)
105 {
106 BMCWEB_LOG_ERROR << ec;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700107 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 else
109 {
110 nlohmann::json &dataJson = *transaction;
Ed Tanous64530012018-02-06 17:08:16 -0800111
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 for (auto &objectPath : objects)
113 {
114 BMCWEB_LOG_DEBUG
115 << "Reading object "
116 << static_cast<const std::string &>(objectPath.first);
117 nlohmann::json &objectJson =
118 dataJson[static_cast<const std::string &>(
119 objectPath.first)];
120 if (objectJson.is_null())
121 {
122 objectJson = nlohmann::json::object();
123 }
124 for (const auto &interface : objectPath.second)
125 {
126 for (const auto &property : interface.second)
127 {
128 nlohmann::json &propertyJson =
129 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700130 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 [&propertyJson](auto &&val) {
132 propertyJson = val;
133 },
134 property.second);
135 }
136 }
137 }
138 }
139
140 if (transaction.use_count() == 1)
141 {
142 res.jsonValue = {{"message", "200 OK"},
143 {"status", "ok"},
144 {"data", std::move(*transaction)}};
145 res.end();
146 }
147 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700148 connection_name, object_manager_path,
149 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
150}
151
152void findObjectManagerPathForEnumerate(
153 const std::string &object_name, const std::string &connection_name,
154 crow::Response &res, std::shared_ptr<nlohmann::json> transaction)
155{
156 crow::connections::systemBus->async_method_call(
157 [&res, transaction, object_name{std::string(object_name)},
158 connection_name{std::string(connection_name)}](
159 const boost::system::error_code ec,
160 const boost::container::flat_map<
161 std::string, boost::container::flat_map<
162 std::string, std::vector<std::string>>>
163 &objects) {
164 if (ec)
165 {
166 BMCWEB_LOG_ERROR << ec;
167 return;
168 }
169
Ed Tanousf254ba72018-10-12 13:40:35 -0700170 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700171 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700172 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700173 {
174 if (connectionGroup.first == connection_name)
175 {
176 // Found the object manager path for this resource.
177 getManagedObjectsForEnumerate(
178 object_name, pathGroup.first, connection_name, res,
179 transaction);
180 return;
181 }
182 }
183 }
184 },
185 "xyz.openbmc_project.ObjectMapper",
186 "/xyz/openbmc_project/object_mapper",
187 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
188 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700189}
Ed Tanous64530012018-02-06 17:08:16 -0800190
191using GetSubTreeType = std::vector<
192 std::pair<std::string,
193 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
194
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700195// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700196struct InProgressActionData
197{
198 InProgressActionData(crow::Response &res) : res(res){};
199 ~InProgressActionData()
200 {
201 if (res.result() == boost::beast::http::status::internal_server_error)
202 {
203 // Reset the json object to clear out any data that made it in
204 // before the error happened todo(ed) handle error condition with
205 // proper code
206 res.jsonValue = nlohmann::json::object();
207 }
208 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700209 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700210
Ed Tanous1abe55e2018-09-05 08:30:59 -0700211 void setErrorStatus()
212 {
213 res.result(boost::beast::http::status::internal_server_error);
214 }
215 crow::Response &res;
216 std::string path;
217 std::string methodName;
218 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700219};
220
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221std::vector<std::string> dbusArgSplit(const std::string &string)
222{
223 std::vector<std::string> ret;
224 if (string.empty())
225 {
226 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700227 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 ret.push_back("");
229 int containerDepth = 0;
230
231 for (std::string::const_iterator character = string.begin();
232 character != string.end(); character++)
233 {
234 ret.back() += *character;
235 switch (*character)
236 {
237 case ('a'):
238 break;
239 case ('('):
240 case ('{'):
241 containerDepth++;
242 break;
243 case ('}'):
244 case (')'):
245 containerDepth--;
246 if (containerDepth == 0)
247 {
248 if (character + 1 != string.end())
249 {
250 ret.push_back("");
251 }
252 }
253 break;
254 default:
255 if (containerDepth == 0)
256 {
257 if (character + 1 != string.end())
258 {
259 ret.push_back("");
260 }
261 }
262 break;
263 }
264 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700265}
266
Ed Tanousd76323e2018-08-07 14:35:40 -0700267int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 const nlohmann::json &input_json)
269{
270 int r = 0;
271 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
272 << " to type: " << arg_type;
273 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700274
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 // Assume a single object for now.
276 const nlohmann::json *j = &input_json;
277 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700278
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700279 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700280 {
281 // If we are decoding multiple objects, grab the pointer to the
282 // iterator, and increment it for the next loop
283 if (argTypes.size() > 1)
284 {
285 if (jIt == input_json.end())
286 {
287 return -2;
288 }
289 j = &*jIt;
290 jIt++;
291 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700292 const int64_t *intValue = j->get_ptr<const int64_t *>();
293 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
294 const std::string *stringValue = j->get_ptr<const std::string *>();
295 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 const bool *b = j->get_ptr<const bool *>();
297 int64_t v = 0;
298 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700299
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 // Do some basic type conversions that make sense. uint can be
301 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700304 v = static_cast<int64_t>(*uintValue);
305 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700306 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700307 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700309 d = static_cast<double>(*uintValue);
310 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700312 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700314 d = static_cast<double>(*intValue);
315 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700316 }
317
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700318 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700320 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700321 {
322 return -1;
323 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700324 r = sd_bus_message_append_basic(m, argCode[0],
325 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326 if (r < 0)
327 {
328 return r;
329 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700330 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700331 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700332 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700333 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700334 {
335 return -1;
336 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700337 int32_t i = static_cast<int32_t>(*intValue);
338 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700339 if (r < 0)
340 {
341 return r;
342 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700343 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700344 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 {
346 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700347 int boolInt = false;
348 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700350 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 }
352 else if (b != nullptr)
353 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700354 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700356 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700357 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700358 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 }
360 else
361 {
362 return -1;
363 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700364 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365 if (r < 0)
366 {
367 return r;
368 }
369 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700370 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700372 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700373 {
374 return -1;
375 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700376 int16_t n = static_cast<int16_t>(*intValue);
377 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700378 if (r < 0)
379 {
380 return r;
381 }
382 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700383 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700385 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700386 {
387 return -1;
388 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700389 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700390 if (r < 0)
391 {
392 return r;
393 }
394 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700395 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700396 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700397 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398 {
399 return -1;
400 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700401 uint8_t y = static_cast<uint8_t>(*uintValue);
402 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700403 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700404 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700406 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 {
408 return -1;
409 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700410 uint16_t q = static_cast<uint16_t>(*uintValue);
411 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700413 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700415 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
417 return -1;
418 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700419 uint32_t u = static_cast<uint32_t>(*uintValue);
420 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700422 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700424 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425 {
426 return -1;
427 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700428 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700429 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700430 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700432 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700433 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700434 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700436 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700438 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 if (r < 0)
440 {
441 return r;
442 }
443
444 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
445 ++it)
446 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700447 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 if (r < 0)
449 {
450 return r;
451 }
452
453 it++;
454 }
455 sd_bus_message_close_container(m);
456 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700457 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700458 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700459 std::string containedType = argCode.substr(1);
460 BMCWEB_LOG_DEBUG << "variant type: " << argCode
461 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700463 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 if (r < 0)
465 {
466 return r;
467 }
468
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700469 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470 if (r < 0)
471 {
472 return r;
473 }
474
475 r = sd_bus_message_close_container(m);
476 if (r < 0)
477 {
478 return r;
479 }
480 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700481 else if (boost::starts_with(argCode, "(") &&
482 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700483 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700484 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700485 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700486 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700488 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489 {
490 if (it == j->end())
491 {
492 return -1;
493 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700494 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700495 if (r < 0)
496 {
497 return r;
498 }
499 it++;
500 }
501 r = sd_bus_message_close_container(m);
502 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700503 else if (boost::starts_with(argCode, "{") &&
504 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700506 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700508 containedType.c_str());
509 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 if (codes.size() != 2)
511 {
512 return -1;
513 }
514 const std::string &key_type = codes[0];
515 const std::string &value_type = codes[1];
516 for (auto it : j->items())
517 {
518 r = convertJsonToDbus(m, key_type, it.key());
519 if (r < 0)
520 {
521 return r;
522 }
523
524 r = convertJsonToDbus(m, value_type, it.value());
525 if (r < 0)
526 {
527 return r;
528 }
529 }
530 r = sd_bus_message_close_container(m);
531 }
532 else
533 {
534 return -2;
535 }
536 if (r < 0)
537 {
538 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700539 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700540
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 if (argTypes.size() > 1)
542 {
543 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700544 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700545 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700546}
547
Ed Tanousd76323e2018-08-07 14:35:40 -0700548void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 const std::string &connectionName)
550{
551 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
552 << connectionName;
553 crow::connections::systemBus->async_method_call(
554 [transaction, connectionName{std::string(connectionName)}](
555 const boost::system::error_code ec,
556 const std::string &introspect_xml) {
557 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
558 if (ec)
559 {
560 BMCWEB_LOG_ERROR
561 << "Introspect call failed with error: " << ec.message()
562 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700563 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 else
565 {
566 tinyxml2::XMLDocument doc;
567
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
570 if (pRoot == nullptr)
571 {
572 BMCWEB_LOG_ERROR << "XML document failed to parse "
573 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700576 tinyxml2::XMLElement *interfaceNode =
577 pRoot->FirstChildElement("interface");
578 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700580 const char *thisInterfaceName =
581 interfaceNode->Attribute("name");
582 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700584 tinyxml2::XMLElement *methodNode =
585 interfaceNode->FirstChildElement("method");
586 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 const char *thisMethodName =
589 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700591 << thisMethodName;
592 if (thisMethodName != nullptr &&
593 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 BMCWEB_LOG_DEBUG
596 << "Found method named " << thisMethodName
597 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 sdbusplus::message::message m =
599 crow::connections::systemBus
600 ->new_method_call(
601 connectionName.c_str(),
602 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700603 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 transaction->methodName.c_str());
605
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 tinyxml2::XMLElement *argumentNode =
607 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 transaction->arguments.begin();
611
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700612 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700614 const char *argDirection =
615 argumentNode->Attribute("direction");
616 const char *argType =
617 argumentNode->Attribute("type");
618 if (argDirection != nullptr &&
619 argType != nullptr &&
620 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622
623 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 transaction->arguments.end())
625 {
626 transaction->setErrorStatus();
627 return;
628 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700629 if (convertJsonToDbus(
630 m.get(), std::string(argType),
631 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 {
633 transaction->setErrorStatus();
634 return;
635 }
636
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 argumentNode =
640 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700642
Ed Tanous1abe55e2018-09-05 08:30:59 -0700643 crow::connections::systemBus->async_send(
644 m, [transaction](
645 boost::system::error_code ec,
646 sdbusplus::message::message &m) {
647 if (ec)
648 {
649 transaction->setErrorStatus();
650 return;
651 }
652 transaction->res.jsonValue = {
653 {"status", "ok"},
654 {"message", "200 OK"},
655 {"data", nullptr}};
656 });
657 break;
658 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700659 methodNode =
660 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700663 interfaceNode =
664 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 }
666 }
667 },
668 connectionName, transaction->path,
669 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700670}
671
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672void handleAction(const crow::Request &req, crow::Response &res,
673 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
676 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 nlohmann::json requestDbusData =
678 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700679
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 if (requestDbusData.is_discarded())
681 {
682 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700683 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 return;
685 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 nlohmann::json::iterator data = requestDbusData.find("data");
687 if (data == requestDbusData.end())
688 {
689 res.result(boost::beast::http::status::bad_request);
690 res.end();
691 return;
692 }
693
694 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 {
696 res.result(boost::beast::http::status::bad_request);
697 res.end();
698 return;
699 }
700 auto transaction = std::make_shared<InProgressActionData>(res);
701
702 transaction->path = objectPath;
703 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 crow::connections::systemBus->async_method_call(
706 [transaction](
707 const boost::system::error_code ec,
708 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700709 &interfaceNames) {
710 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700712 BMCWEB_LOG_ERROR << "Can't find object";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 transaction->setErrorStatus();
714 return;
715 }
716
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700717 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
718 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719
720 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700721 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 {
723 findActionOnInterface(transaction, object.first);
724 }
725 },
726 "xyz.openbmc_project.ObjectMapper",
727 "/xyz/openbmc_project/object_mapper",
728 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
729 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700730}
731
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700732void handleList(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733{
734 crow::connections::systemBus->async_method_call(
735 [&res](const boost::system::error_code ec,
736 std::vector<std::string> &objectPaths) {
737 if (ec)
738 {
739 res.result(boost::beast::http::status::internal_server_error);
740 }
741 else
742 {
743 res.jsonValue = {{"status", "ok"},
744 {"message", "200 OK"},
745 {"data", std::move(objectPaths)}};
746 }
747 res.end();
748 },
749 "xyz.openbmc_project.ObjectMapper",
750 "/xyz/openbmc_project/object_mapper",
751 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700752 static_cast<int32_t>(0), std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700754
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700755void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756{
757 crow::connections::systemBus->async_method_call(
758 [&res, objectPath{std::string(objectPath)}](
759 const boost::system::error_code ec,
760 const GetSubTreeType &object_names) {
761 if (ec)
762 {
763 res.jsonValue = {{"message", "200 OK"},
764 {"status", "ok"},
765 {"data", nlohmann::json::object()}};
Ed Tanous64530012018-02-06 17:08:16 -0800766
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 res.end();
768 return;
769 }
Ed Tanous64530012018-02-06 17:08:16 -0800770
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 boost::container::flat_set<std::string> connections;
Ed Tanous64530012018-02-06 17:08:16 -0800772
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 for (const auto &object : object_names)
774 {
775 for (const auto &Connection : object.second)
776 {
777 connections.insert(Connection.first);
778 }
779 }
780
781 if (connections.size() <= 0)
782 {
783 res.result(boost::beast::http::status::not_found);
784 res.end();
785 return;
786 }
787 auto transaction =
788 std::make_shared<nlohmann::json>(nlohmann::json::object());
789 for (const std::string &Connection : connections)
790 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700791 findObjectManagerPathForEnumerate(objectPath, Connection, res,
792 transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 }
794 },
795 "xyz.openbmc_project.ObjectMapper",
796 "/xyz/openbmc_project/object_mapper",
797 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
798 (int32_t)0, std::array<std::string, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800799}
Ed Tanous911ac312017-08-15 09:37:42 -0700800
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700801void handleGet(crow::Response &res, std::string &objectPath,
802 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700803{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700804 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
805 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -0700807
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 std::shared_ptr<std::string> path =
809 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -0700810
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 using GetObjectType =
812 std::vector<std::pair<std::string, std::vector<std::string>>>;
813 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814 [&res, path, propertyName](const boost::system::error_code ec,
815 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 if (ec || object_names.size() <= 0)
817 {
818 res.result(boost::beast::http::status::not_found);
819 res.end();
820 return;
821 }
822 std::shared_ptr<nlohmann::json> response =
823 std::make_shared<nlohmann::json>(nlohmann::json::object());
824 // The mapper should never give us an empty interface names list,
825 // but check anyway
826 for (const std::pair<std::string, std::vector<std::string>>
827 connection : object_names)
828 {
829 const std::vector<std::string> &interfaceNames =
830 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700831
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 if (interfaceNames.size() <= 0)
833 {
834 res.result(boost::beast::http::status::not_found);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700835 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836 return;
837 }
838
839 for (const std::string &interface : interfaceNames)
840 {
841 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700842 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700844 const std::vector<std::pair<
845 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 &properties) {
847 if (ec)
848 {
849 BMCWEB_LOG_ERROR << "Bad dbus request error: "
850 << ec;
851 }
852 else
853 {
James Feist5b4aa862018-08-16 14:07:01 -0700854 for (const std::pair<
855 std::string,
856 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 &property : properties)
858 {
859 // if property name is empty, or matches our
860 // search query, add it to the response json
861
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700862 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700864 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865 [&response, &property](auto &&val) {
866 (*response)[property.first] =
867 val;
868 },
869 property.second);
870 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700871 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700873 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 [&response](auto &&val) {
875 (*response) = val;
876 },
877 property.second);
878 }
879 }
880 }
881 if (response.use_count() == 1)
882 {
883 res.jsonValue = {{"status", "ok"},
884 {"message", "200 OK"},
885 {"data", *response}};
886
887 res.end();
888 }
889 },
890 connection.first, *path,
891 "org.freedesktop.DBus.Properties", "GetAll", interface);
892 }
893 }
894 },
895 "xyz.openbmc_project.ObjectMapper",
896 "/xyz/openbmc_project/object_mapper",
897 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
898 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700899}
900
Ed Tanous1abe55e2018-09-05 08:30:59 -0700901struct AsyncPutRequest
902{
903 AsyncPutRequest(crow::Response &res) : res(res)
904 {
905 res.jsonValue = {
906 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
907 }
908 ~AsyncPutRequest()
909 {
910 if (res.result() == boost::beast::http::status::internal_server_error)
911 {
912 // Reset the json object to clear out any data that made it in
913 // before the error happened todo(ed) handle error condition with
914 // proper code
915 res.jsonValue = nlohmann::json::object();
916 }
917
918 if (res.jsonValue.empty())
919 {
920 res.result(boost::beast::http::status::forbidden);
921 res.jsonValue = {
922 {"status", "error"},
923 {"message", "403 Forbidden"},
924 {"data",
925 {{"message", "The specified property cannot be created: " +
926 propertyName}}}};
927 }
928
929 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700930 }
931
Ed Tanous1abe55e2018-09-05 08:30:59 -0700932 void setErrorStatus()
933 {
934 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700935 }
936
Ed Tanous1abe55e2018-09-05 08:30:59 -0700937 crow::Response &res;
938 std::string objectPath;
939 std::string propertyName;
940 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700941};
942
Ed Tanousd76323e2018-08-07 14:35:40 -0700943void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700944 const std::string &objectPath, const std::string &destProperty)
945{
946 nlohmann::json requestDbusData =
947 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700948
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 if (requestDbusData.is_discarded())
950 {
951 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700952 res.end();
953 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700954 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700955
Ed Tanous1abe55e2018-09-05 08:30:59 -0700956 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
957 if (propertyIt == requestDbusData.end())
958 {
959 res.result(boost::beast::http::status::bad_request);
960 res.end();
961 return;
962 }
963 const nlohmann::json &propertySetValue = *propertyIt;
964 auto transaction = std::make_shared<AsyncPutRequest>(res);
965 transaction->objectPath = objectPath;
966 transaction->propertyName = destProperty;
967 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -0700968
Ed Tanous1abe55e2018-09-05 08:30:59 -0700969 using GetObjectType =
970 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -0700971
Ed Tanous1abe55e2018-09-05 08:30:59 -0700972 crow::connections::systemBus->async_method_call(
973 [transaction](const boost::system::error_code ec,
974 const GetObjectType &object_names) {
975 if (!ec && object_names.size() <= 0)
976 {
977 transaction->res.result(boost::beast::http::status::not_found);
978 return;
979 }
Ed Tanous911ac312017-08-15 09:37:42 -0700980
Ed Tanous1abe55e2018-09-05 08:30:59 -0700981 for (const std::pair<std::string, std::vector<std::string>>
982 connection : object_names)
983 {
984 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -0700985
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986 crow::connections::systemBus->async_method_call(
987 [connectionName{std::string(connectionName)},
988 transaction](const boost::system::error_code ec,
989 const std::string &introspectXml) {
990 if (ec)
991 {
992 BMCWEB_LOG_ERROR
993 << "Introspect call failed with error: "
994 << ec.message()
995 << " on process: " << connectionName;
996 transaction->setErrorStatus();
997 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700998 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001000
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001 doc.Parse(introspectXml.c_str());
1002 tinyxml2::XMLNode *pRoot =
1003 doc.FirstChildElement("node");
1004 if (pRoot == nullptr)
1005 {
1006 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1007 << introspectXml;
1008 transaction->setErrorStatus();
1009 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001010 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001011 tinyxml2::XMLElement *ifaceNode =
1012 pRoot->FirstChildElement("interface");
1013 while (ifaceNode != nullptr)
1014 {
1015 const char *interfaceName =
1016 ifaceNode->Attribute("name");
1017 BMCWEB_LOG_DEBUG << "found interface "
1018 << interfaceName;
1019 tinyxml2::XMLElement *propNode =
1020 ifaceNode->FirstChildElement("property");
1021 while (propNode != nullptr)
1022 {
1023 const char *propertyName =
1024 propNode->Attribute("name");
1025 BMCWEB_LOG_DEBUG << "Found property "
1026 << propertyName;
1027 if (propertyName == transaction->propertyName)
1028 {
1029 const char *argType =
1030 propNode->Attribute("type");
1031 if (argType != nullptr)
1032 {
1033 sdbusplus::message::message m =
1034 crow::connections::systemBus
1035 ->new_method_call(
1036 connectionName.c_str(),
1037 transaction->objectPath
1038 .c_str(),
1039 "org.freedesktop.DBus."
1040 "Properties",
1041 "Set");
1042 m.append(interfaceName,
1043 transaction->propertyName);
1044 int r = sd_bus_message_open_container(
1045 m.get(), SD_BUS_TYPE_VARIANT,
1046 argType);
1047 if (r < 0)
1048 {
1049 transaction->setErrorStatus();
1050 return;
1051 }
1052 r = convertJsonToDbus(
1053 m.get(), argType,
1054 transaction->propertyValue);
1055 if (r < 0)
1056 {
1057 transaction->setErrorStatus();
1058 return;
1059 }
1060 r = sd_bus_message_close_container(
1061 m.get());
1062 if (r < 0)
1063 {
1064 transaction->setErrorStatus();
1065 return;
1066 }
Ed Tanous911ac312017-08-15 09:37:42 -07001067
Ed Tanous1abe55e2018-09-05 08:30:59 -07001068 crow::connections::systemBus
1069 ->async_send(
1070 m,
1071 [transaction](
1072 boost::system::error_code
1073 ec,
1074 sdbusplus::message::message
1075 &m) {
1076 BMCWEB_LOG_DEBUG << "sent";
1077 if (ec)
1078 {
1079 transaction->res
1080 .jsonValue
1081 ["status"] =
1082 "error";
1083 transaction->res
1084 .jsonValue
1085 ["message"] =
1086 ec.message();
1087 }
1088 });
1089 }
1090 }
1091 propNode =
1092 propNode->NextSiblingElement("property");
1093 }
1094 ifaceNode =
1095 ifaceNode->NextSiblingElement("interface");
1096 }
1097 },
1098 connectionName, transaction->objectPath,
1099 "org.freedesktop.DBus.Introspectable", "Introspect");
1100 }
1101 },
1102 "xyz.openbmc_project.ObjectMapper",
1103 "/xyz/openbmc_project/object_mapper",
1104 "xyz.openbmc_project.ObjectMapper", "GetObject",
1105 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001106}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001107
1108template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1109{
1110 BMCWEB_ROUTE(app, "/bus/")
1111 .methods("GET"_method)(
1112 [](const crow::Request &req, crow::Response &res) {
1113 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1114 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001115 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116 });
1117
1118 BMCWEB_ROUTE(app, "/bus/system/")
1119 .methods("GET"_method)(
1120 [](const crow::Request &req, crow::Response &res) {
1121 auto myCallback = [&res](const boost::system::error_code ec,
1122 std::vector<std::string> &names) {
1123 if (ec)
1124 {
1125 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1126 res.result(
1127 boost::beast::http::status::internal_server_error);
1128 }
1129 else
1130 {
1131 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001132 res.jsonValue = {{"status", "ok"}};
1133 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001134 for (auto &name : names)
1135 {
1136 objectsSub.push_back({{"name", name}});
1137 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001138 }
1139 res.end();
1140 };
1141 crow::connections::systemBus->async_method_call(
1142 std::move(myCallback), "org.freedesktop.DBus", "/",
1143 "org.freedesktop.DBus", "ListNames");
1144 });
1145
1146 BMCWEB_ROUTE(app, "/list/")
1147 .methods("GET"_method)(
1148 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001149 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 });
1151
1152 BMCWEB_ROUTE(app, "/xyz/<path>")
1153 .methods("GET"_method, "PUT"_method,
1154 "POST"_method)([](const crow::Request &req,
1155 crow::Response &res,
1156 const std::string &path) {
1157 std::string objectPath = "/xyz/" + path;
1158
1159 // Trim any trailing "/" at the end
1160 if (boost::ends_with(objectPath, "/"))
1161 {
1162 objectPath.pop_back();
1163 }
1164
1165 // If accessing a single attribute, fill in and update objectPath,
1166 // otherwise leave destProperty blank
1167 std::string destProperty = "";
1168 const char *attrSeperator = "/attr/";
1169 size_t attrPosition = path.find(attrSeperator);
1170 if (attrPosition != path.npos)
1171 {
1172 objectPath = "/xyz/" + path.substr(0, attrPosition);
1173 destProperty = path.substr(attrPosition + strlen(attrSeperator),
1174 path.length());
1175 }
1176
1177 if (req.method() == "POST"_method)
1178 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001179 constexpr const char *actionSeperator = "/action/";
1180 size_t actionPosition = path.find(actionSeperator);
1181 if (actionPosition != path.npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001182 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001183 objectPath = "/xyz/" + path.substr(0, actionPosition);
1184 std::string postProperty =
1185 path.substr((actionPosition + strlen(actionSeperator)),
1186 path.length());
1187 handleAction(req, res, objectPath, postProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001188 return;
1189 }
1190 }
1191 else if (req.method() == "GET"_method)
1192 {
1193 if (boost::ends_with(objectPath, "/enumerate"))
1194 {
1195 objectPath.erase(objectPath.end() - 10, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001196 handleEnumerate(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001197 }
1198 else if (boost::ends_with(objectPath, "/list"))
1199 {
1200 objectPath.erase(objectPath.end() - 5, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001201 handleList(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001202 }
1203 else
1204 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001205 handleGet(res, objectPath, destProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001206 }
1207 return;
1208 }
1209 else if (req.method() == "PUT"_method)
1210 {
1211 handlePut(req, res, objectPath, destProperty);
1212 return;
1213 }
1214
1215 res.result(boost::beast::http::status::method_not_allowed);
1216 res.end();
1217 });
1218
Ed Tanous1abe55e2018-09-05 08:30:59 -07001219 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1220 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1221 const std::string &dumpId) {
1222 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1223 if (!std::regex_match(dumpId, validFilename))
1224 {
1225 res.result(boost::beast::http::status::not_found);
1226 res.end();
1227 return;
1228 }
1229 std::experimental::filesystem::path loc(
1230 "/var/lib/phosphor-debug-collector/dumps");
1231
1232 loc += dumpId;
1233
1234 if (!std::experimental::filesystem::exists(loc) ||
1235 !std::experimental::filesystem::is_directory(loc))
1236 {
1237 res.result(boost::beast::http::status::not_found);
1238 res.end();
1239 return;
1240 }
1241 std::experimental::filesystem::directory_iterator files(loc);
1242 for (auto &file : files)
1243 {
1244 std::ifstream readFile(file.path());
1245 if (readFile.good())
1246 {
1247 continue;
1248 }
1249 res.addHeader("Content-Type", "application/octet-stream");
1250 res.body() = {std::istreambuf_iterator<char>(readFile),
1251 std::istreambuf_iterator<char>()};
1252 res.end();
1253 }
1254 res.result(boost::beast::http::status::not_found);
1255 res.end();
1256 return;
1257 });
1258
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001259 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001261 const std::string &Connection) {
1262 introspectObjects(Connection, "/",
1263 std::make_shared<bmcweb::AsyncResp>(res));
1264 });
1265
1266 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1267 .methods("GET"_method,
1268 "POST"_method)([](const crow::Request &req,
1269 crow::Response &res,
1270 const std::string &processName,
1271 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001272 std::vector<std::string> strs;
1273 boost::split(strs, requestedPath, boost::is_any_of("/"));
1274 std::string objectPath;
1275 std::string interfaceName;
1276 std::string methodName;
1277 auto it = strs.begin();
1278 if (it == strs.end())
1279 {
1280 objectPath = "/";
1281 }
1282 while (it != strs.end())
1283 {
1284 // Check if segment contains ".". If it does, it must be an
1285 // interface
1286 if (it->find(".") != std::string::npos)
1287 {
1288 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001289 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290 // as part of our <path> specifier above, which causes the
1291 // normal trailing backslash redirector to fail.
1292 }
1293 else if (!it->empty())
1294 {
1295 objectPath += "/" + *it;
1296 }
1297 it++;
1298 }
1299 if (it != strs.end())
1300 {
1301 interfaceName = *it;
1302 it++;
1303
1304 // after interface, we might have a method name
1305 if (it != strs.end())
1306 {
1307 methodName = *it;
1308 it++;
1309 }
1310 }
1311 if (it != strs.end())
1312 {
1313 // if there is more levels past the method name, something went
1314 // wrong, return not found
1315 res.result(boost::beast::http::status::not_found);
1316 res.end();
1317 return;
1318 }
1319 if (interfaceName.empty())
1320 {
1321 crow::connections::systemBus->async_method_call(
1322 [&, processName,
1323 objectPath](const boost::system::error_code ec,
1324 const std::string &introspect_xml) {
1325 if (ec)
1326 {
1327 BMCWEB_LOG_ERROR
1328 << "Introspect call failed with error: "
1329 << ec.message()
1330 << " on process: " << processName
1331 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001332 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001333 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001334 tinyxml2::XMLDocument doc;
1335
1336 doc.Parse(introspect_xml.c_str());
1337 tinyxml2::XMLNode *pRoot =
1338 doc.FirstChildElement("node");
1339 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001340 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001341 BMCWEB_LOG_ERROR << "XML document failed to parse "
1342 << processName << " " << objectPath
1343 << "\n";
1344 res.jsonValue = {{"status", "XML parse error"}};
1345 res.result(boost::beast::http::status::
1346 internal_server_error);
1347 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001349
1350 BMCWEB_LOG_DEBUG << introspect_xml;
1351 res.jsonValue = {{"status", "ok"},
1352 {"bus_name", processName},
1353 {"object_path", objectPath}};
1354 nlohmann::json &interfacesArray =
1355 res.jsonValue["interfaces"];
1356 interfacesArray = nlohmann::json::array();
1357 tinyxml2::XMLElement *interface =
1358 pRoot->FirstChildElement("interface");
1359
1360 while (interface != nullptr)
1361 {
1362 const char *ifaceName =
1363 interface->Attribute("name");
1364 if (ifaceName != nullptr)
1365 {
1366 interfacesArray.push_back(
1367 {{"name", ifaceName}});
1368 }
1369
1370 interface =
1371 interface->NextSiblingElement("interface");
1372 }
1373
Ed Tanous1abe55e2018-09-05 08:30:59 -07001374 res.end();
1375 },
1376 processName, objectPath,
1377 "org.freedesktop.DBus.Introspectable", "Introspect");
1378 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001379 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001380 {
1381 crow::connections::systemBus->async_method_call(
1382 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001383 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001384 const boost::system::error_code ec,
1385 const std::string &introspect_xml) {
1386 if (ec)
1387 {
1388 BMCWEB_LOG_ERROR
1389 << "Introspect call failed with error: "
1390 << ec.message()
1391 << " on process: " << processName
1392 << " path: " << objectPath << "\n";
1393 }
1394 else
1395 {
1396 tinyxml2::XMLDocument doc;
1397
1398 doc.Parse(introspect_xml.c_str());
1399 tinyxml2::XMLNode *pRoot =
1400 doc.FirstChildElement("node");
1401 if (pRoot == nullptr)
1402 {
1403 BMCWEB_LOG_ERROR
1404 << "XML document failed to parse "
1405 << processName << " " << objectPath << "\n";
1406 res.result(boost::beast::http::status::
1407 internal_server_error);
1408 }
1409 else
1410 {
1411 tinyxml2::XMLElement *node =
1412 pRoot->FirstChildElement("node");
1413
1414 // if we know we're the only call, build the
1415 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416 tinyxml2::XMLElement *interface =
1417 pRoot->FirstChildElement("interface");
1418
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001419 res.jsonValue = {
1420 {"status", "ok"},
1421 {"bus_name", processName},
1422 {"interface", interfaceName},
1423 {"object_path", objectPath},
1424 {"properties", nlohmann::json::object()}};
1425
1426 nlohmann::json &methodsArray =
1427 res.jsonValue["methods"];
1428 methodsArray = nlohmann::json::array();
1429
1430 nlohmann::json &signalsArray =
1431 res.jsonValue["signals"];
1432 signalsArray = nlohmann::json::array();
1433
Ed Tanous1abe55e2018-09-05 08:30:59 -07001434 while (interface != nullptr)
1435 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001436 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001437 interface->Attribute("name");
1438
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001439 if (ifaceName != nullptr &&
1440 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001441 {
1442 tinyxml2::XMLElement *methods =
1443 interface->FirstChildElement(
1444 "method");
1445 while (methods != nullptr)
1446 {
1447 nlohmann::json argsArray =
1448 nlohmann::json::array();
1449 tinyxml2::XMLElement *arg =
1450 methods->FirstChildElement(
1451 "arg");
1452 while (arg != nullptr)
1453 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001454 nlohmann::json thisArg;
1455 for (const char *fieldName :
1456 std::array<const char *,
1457 3>{"name",
1458 "direction",
1459 "type"})
1460 {
1461 const char *fieldValue =
1462 arg->Attribute(
1463 fieldName);
1464 if (fieldValue != nullptr)
1465 {
1466 thisArg[fieldName] =
1467 fieldValue;
1468 }
1469 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001470 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001471 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001472 arg = arg->NextSiblingElement(
1473 "arg");
1474 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001475
1476 const char *name =
1477 methods->Attribute("name");
1478 if (name != nullptr)
1479 {
1480 methodsArray.push_back(
1481 {{"name", name},
1482 {"uri", "/bus/system/" +
1483 processName +
1484 objectPath +
1485 "/" +
1486 interfaceName +
1487 "/" + name},
1488 {"args", argsArray}});
1489 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 methods =
1491 methods->NextSiblingElement(
1492 "method");
1493 }
1494 tinyxml2::XMLElement *signals =
1495 interface->FirstChildElement(
1496 "signal");
1497 while (signals != nullptr)
1498 {
1499 nlohmann::json argsArray =
1500 nlohmann::json::array();
1501
1502 tinyxml2::XMLElement *arg =
1503 signals->FirstChildElement(
1504 "arg");
1505 while (arg != nullptr)
1506 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001507 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001508 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001509 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001510 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001511 if (name != nullptr &&
1512 type != nullptr)
1513 {
1514 argsArray.push_back({
1515 {"name", name},
1516 {"type", type},
1517 });
1518 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001519 arg = arg->NextSiblingElement(
1520 "arg");
1521 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001522 const char *name =
1523 signals->Attribute("name");
1524 if (name != nullptr)
1525 {
1526 signalsArray.push_back(
1527 {{"name", name},
1528 {"args", argsArray}});
1529 }
1530
Ed Tanous1abe55e2018-09-05 08:30:59 -07001531 signals =
1532 signals->NextSiblingElement(
1533 "signal");
1534 }
1535
Ed Tanous1abe55e2018-09-05 08:30:59 -07001536 break;
1537 }
1538
1539 interface = interface->NextSiblingElement(
1540 "interface");
1541 }
1542 if (interface == nullptr)
1543 {
1544 // if we got to the end of the list and
1545 // never found a match, throw 404
1546 res.result(
1547 boost::beast::http::status::not_found);
1548 }
1549 }
1550 }
1551 res.end();
1552 },
1553 processName, objectPath,
1554 "org.freedesktop.DBus.Introspectable", "Introspect");
1555 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001556 else
1557 {
1558 if (req.method() != "POST"_method)
1559 {
1560 res.result(boost::beast::http::status::not_found);
1561 res.end();
1562 return;
1563 }
1564
1565 nlohmann::json requestDbusData =
1566 nlohmann::json::parse(req.body, nullptr, false);
1567
1568 if (requestDbusData.is_discarded())
1569 {
1570 res.result(boost::beast::http::status::bad_request);
1571 res.end();
1572 return;
1573 }
1574 if (!requestDbusData.is_array())
1575 {
1576 res.result(boost::beast::http::status::bad_request);
1577 res.end();
1578 return;
1579 }
1580 auto transaction = std::make_shared<InProgressActionData>(res);
1581
1582 transaction->path = objectPath;
1583 transaction->methodName = methodName;
1584 transaction->arguments = std::move(requestDbusData);
1585
1586 findActionOnInterface(transaction, processName);
1587 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588 });
1589}
1590} // namespace openbmc_mapper
1591} // namespace crow