blob: 605d6f9d72a2de3adf57980075853992f7111cd2 [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
James Feistf6150402019-01-08 10:36:20 -080016#include "filesystem.hpp"
17
Ed Tanous911ac312017-08-15 09:37:42 -070018#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070019#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020
Ed Tanouse3cb5a32018-08-08 14:16:49 -070021#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070022#include <boost/algorithm/string.hpp>
23#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070024#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070025#include <dbus_utility.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070026#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070027#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace openbmc_mapper
32{
Ed Tanousba9f9a62017-10-11 16:40:35 -070033
Matt Spinler3ae4ba72018-12-05 14:01:22 -060034using GetSubTreeType = std::vector<
35 std::pair<std::string,
36 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37
Matt Spinler2ae60092018-12-06 10:35:36 -060038const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060039const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060040const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060041const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060042const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler16caaee2019-01-15 11:40:34 -060043const std::string methodOutputFailedMsg = "500 Method Output Error";
Matt Spinler6db06242018-12-11 11:21:22 -060044
Matt Spinler2ae60092018-12-06 10:35:36 -060045const std::string notFoundDesc =
46 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060047const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060048const std::string noJsonDesc = "No JSON object could be decoded";
49const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060050const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060051const std::string forbiddenPropDesc =
52 "The specified property cannot be created";
53const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060054
55void setErrorResponse(crow::Response &res, boost::beast::http::status result,
56 const std::string &desc, const std::string &msg)
57{
58 res.result(result);
59 res.jsonValue = {{"data", {{"description", desc}}},
60 {"message", msg},
61 {"status", "error"}};
62}
63
Ed Tanouse3cb5a32018-08-08 14:16:49 -070064void introspectObjects(const std::string &processName,
65 const std::string &objectPath,
66 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070068 if (transaction->res.jsonValue.is_null())
69 {
70 transaction->res.jsonValue = {{"status", "ok"},
71 {"bus_name", processName},
72 {"objects", nlohmann::json::array()}};
73 }
74
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070076 [transaction, processName{std::string(processName)},
77 objectPath{std::string(objectPath)}](
78 const boost::system::error_code ec,
79 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 if (ec)
81 {
82 BMCWEB_LOG_ERROR
83 << "Introspect call failed with error: " << ec.message()
84 << " on process: " << processName << " path: " << objectPath
85 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070086 return;
87 }
88 transaction->res.jsonValue["objects"].push_back(
89 {{"path", objectPath}});
90
91 tinyxml2::XMLDocument doc;
92
93 doc.Parse(introspect_xml.c_str());
94 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
95 if (pRoot == nullptr)
96 {
97 BMCWEB_LOG_ERROR << "XML document failed to parse "
98 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070099 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 else
101 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
103 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700105 const char *childPath = node->Attribute("name");
106 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 std::string newpath;
109 if (objectPath != "/")
110 {
111 newpath += objectPath;
112 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700115 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700117
118 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 }
120 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700124}
Ed Tanous64530012018-02-06 17:08:16 -0800125
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600126void getPropertiesForEnumerate(const std::string &objectPath,
127 const std::string &service,
128 const std::string &interface,
129 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
130{
131 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
132 << service << " " << interface;
133
134 crow::connections::systemBus->async_method_call(
135 [asyncResp, objectPath, service,
136 interface](const boost::system::error_code ec,
137 const std::vector<
138 std::pair<std::string, dbus::utility::DbusVariantType>>
139 &propertiesList) {
140 if (ec)
141 {
142 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
143 << interface << " service " << service
144 << " failed with code " << ec;
145 return;
146 }
147
148 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
149 nlohmann::json &objectJson = dataJson[objectPath];
150 if (objectJson.is_null())
151 {
152 objectJson = nlohmann::json::object();
153 }
154
155 for (const auto &[name, value] : propertiesList)
156 {
157 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800158 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
159 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600160 }
161 },
162 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
163 interface);
164}
165
166// Find any results that weren't picked up by ObjectManagers, to be
167// called after all ObjectManagers are searched for and called.
168void findRemainingObjectsForEnumerate(
169 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
170 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
171{
172 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
173 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
174
175 for (const auto &[path, interface_map] : *subtree)
176 {
177 if (path == objectPath)
178 {
179 // An enumerate does not return the target path's properties
180 continue;
181 }
182 if (dataJson.find(path) == dataJson.end())
183 {
184 for (const auto &[service, interfaces] : interface_map)
185 {
186 for (const auto &interface : interfaces)
187 {
188 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
189 {
190 getPropertiesForEnumerate(path, service, interface,
191 asyncResp);
192 }
193 }
194 }
195 }
196 }
197}
198
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600199struct InProgressEnumerateData
200{
201 InProgressEnumerateData(const std::string &objectPath,
202 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
203 objectPath(objectPath),
204 asyncResp(asyncResp)
205 {
206 }
207
208 ~InProgressEnumerateData()
209 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600210 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600211 }
212
213 const std::string objectPath;
214 std::shared_ptr<GetSubTreeType> subtree;
215 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
216};
217
218void getManagedObjectsForEnumerate(
219 const std::string &object_name, const std::string &object_manager_path,
220 const std::string &connection_name,
221 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222{
Ed Tanous049a0512018-11-01 13:58:42 -0700223 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
224 << " object_manager_path " << object_manager_path
225 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600227 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700228 connection_name](const boost::system::error_code ec,
229 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 if (ec)
231 {
Ed Tanous049a0512018-11-01 13:58:42 -0700232 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600233 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700234 << " failed with code " << ec;
235 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700236 }
Ed Tanous64530012018-02-06 17:08:16 -0800237
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600238 nlohmann::json &dataJson =
239 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700240
241 for (const auto &objectPath : objects)
242 {
243 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 {
Ed Tanous049a0512018-11-01 13:58:42 -0700245 BMCWEB_LOG_DEBUG << "Reading object "
246 << objectPath.first.str;
247 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 if (objectJson.is_null())
249 {
250 objectJson = nlohmann::json::object();
251 }
252 for (const auto &interface : objectPath.second)
253 {
254 for (const auto &property : interface.second)
255 {
256 nlohmann::json &propertyJson =
257 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800258 std::visit([&propertyJson](
259 auto &&val) { propertyJson = val; },
260 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 }
262 }
263 }
Ed Tanous049a0512018-11-01 13:58:42 -0700264 for (const auto &interface : objectPath.second)
265 {
266 if (interface.first == "org.freedesktop.DBus.ObjectManager")
267 {
268 getManagedObjectsForEnumerate(
269 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600270 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700271 }
272 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 }
274 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700275 connection_name, object_manager_path,
276 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
277}
278
279void findObjectManagerPathForEnumerate(
280 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600281 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700282{
Ed Tanous049a0512018-11-01 13:58:42 -0700283 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
284 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700285 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600286 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 const boost::system::error_code ec,
288 const boost::container::flat_map<
289 std::string, boost::container::flat_map<
290 std::string, std::vector<std::string>>>
291 &objects) {
292 if (ec)
293 {
Ed Tanous049a0512018-11-01 13:58:42 -0700294 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
295 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700296 return;
297 }
298
Ed Tanousf254ba72018-10-12 13:40:35 -0700299 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700300 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700301 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
303 if (connectionGroup.first == connection_name)
304 {
305 // Found the object manager path for this resource.
306 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700307 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600308 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700309 return;
310 }
311 }
312 }
313 },
314 "xyz.openbmc_project.ObjectMapper",
315 "/xyz/openbmc_project/object_mapper",
316 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
317 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700318}
Ed Tanous64530012018-02-06 17:08:16 -0800319
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600320// Uses GetObject to add the object info about the target /enumerate path to the
321// results of GetSubTree, as GetSubTree will not return info for the
322// target path, and then continues on enumerating the rest of the tree.
323void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
324{
325 using GetObjectType =
326 std::vector<std::pair<std::string, std::vector<std::string>>>;
327
328 crow::connections::systemBus->async_method_call(
329 [transaction](const boost::system::error_code ec,
330 const GetObjectType &objects) {
331 if (ec)
332 {
333 BMCWEB_LOG_ERROR << "GetObject for path "
334 << transaction->objectPath
335 << " failed with code " << ec;
336 return;
337 }
338
339 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
340 << " has " << objects.size() << " entries";
341 if (!objects.empty())
342 {
343 transaction->subtree->emplace_back(transaction->objectPath,
344 objects);
345 }
346
347 // Map indicating connection name, and the path where the object
348 // manager exists
349 boost::container::flat_map<std::string, std::string> connections;
350
351 for (const auto &object : *(transaction->subtree))
352 {
353 for (const auto &connection : object.second)
354 {
355 std::string &objectManagerPath =
356 connections[connection.first];
357 for (const auto &interface : connection.second)
358 {
359 BMCWEB_LOG_DEBUG << connection.first
360 << " has interface " << interface;
361 if (interface == "org.freedesktop.DBus.ObjectManager")
362 {
363 BMCWEB_LOG_DEBUG << "found object manager path "
364 << object.first;
365 objectManagerPath = object.first;
366 }
367 }
368 }
369 }
370 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
371
372 for (const auto &connection : connections)
373 {
374 // If we already know where the object manager is, we don't need
375 // to search for it, we can call directly in to
376 // getManagedObjects
377 if (!connection.second.empty())
378 {
379 getManagedObjectsForEnumerate(
380 transaction->objectPath, connection.second,
381 connection.first, transaction);
382 }
383 else
384 {
385 // otherwise we need to find the object manager path before
386 // we can continue
387 findObjectManagerPathForEnumerate(
388 transaction->objectPath, connection.first, transaction);
389 }
390 }
391 },
392 "xyz.openbmc_project.ObjectMapper",
393 "/xyz/openbmc_project/object_mapper",
394 "xyz.openbmc_project.ObjectMapper", "GetObject",
395 transaction->objectPath, std::array<const char *, 0>());
396}
Ed Tanous64530012018-02-06 17:08:16 -0800397
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700398// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399struct InProgressActionData
400{
401 InProgressActionData(crow::Response &res) : res(res){};
402 ~InProgressActionData()
403 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600404 // Methods could have been called across different owners
405 // and interfaces, where some calls failed and some passed.
406 //
407 // The rules for this are:
408 // * if no method was called - error
409 // * if a method failed and none passed - error
410 // (converse: if at least one method passed - OK)
411 // * for the method output:
412 // * if output processing didn't fail, return the data
413
414 // Only deal with method returns if nothing failed earlier
415 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600417 if (!methodPassed)
418 {
419 if (methodFailed)
420 {
421 setErrorResponse(res,
422 boost::beast::http::status::bad_request,
423 "Method call failed", methodFailedMsg);
424 }
425 else
426 {
427 setErrorResponse(res, boost::beast::http::status::not_found,
428 methodNotFoundDesc, notFoundMsg);
429 }
430 }
431 else
432 {
433 if (outputFailed)
434 {
435 setErrorResponse(
436 res, boost::beast::http::status::internal_server_error,
437 "Method output failure", methodOutputFailedMsg);
438 }
439 else
440 {
441 res.jsonValue = {{"status", "ok"},
442 {"message", "200 OK"},
443 {"data", methodResponse}};
444 }
445 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600447
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700449 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700450
Matt Spinler6db06242018-12-11 11:21:22 -0600451 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600453 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
454 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 }
456 crow::Response &res;
457 std::string path;
458 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600459 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600460 bool methodPassed = false;
461 bool methodFailed = false;
462 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600463 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600464 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700466};
467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468std::vector<std::string> dbusArgSplit(const std::string &string)
469{
470 std::vector<std::string> ret;
471 if (string.empty())
472 {
473 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700474 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700475 ret.push_back("");
476 int containerDepth = 0;
477
478 for (std::string::const_iterator character = string.begin();
479 character != string.end(); character++)
480 {
481 ret.back() += *character;
482 switch (*character)
483 {
484 case ('a'):
485 break;
486 case ('('):
487 case ('{'):
488 containerDepth++;
489 break;
490 case ('}'):
491 case (')'):
492 containerDepth--;
493 if (containerDepth == 0)
494 {
495 if (character + 1 != string.end())
496 {
497 ret.push_back("");
498 }
499 }
500 break;
501 default:
502 if (containerDepth == 0)
503 {
504 if (character + 1 != string.end())
505 {
506 ret.push_back("");
507 }
508 }
509 break;
510 }
511 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600512
513 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700514}
515
Ed Tanousd76323e2018-08-07 14:35:40 -0700516int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 const nlohmann::json &input_json)
518{
519 int r = 0;
520 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
521 << " to type: " << arg_type;
522 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700523
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 // Assume a single object for now.
525 const nlohmann::json *j = &input_json;
526 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700527
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700528 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529 {
530 // If we are decoding multiple objects, grab the pointer to the
531 // iterator, and increment it for the next loop
532 if (argTypes.size() > 1)
533 {
534 if (jIt == input_json.end())
535 {
536 return -2;
537 }
538 j = &*jIt;
539 jIt++;
540 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700541 const int64_t *intValue = j->get_ptr<const int64_t *>();
542 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
543 const std::string *stringValue = j->get_ptr<const std::string *>();
544 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 const bool *b = j->get_ptr<const bool *>();
546 int64_t v = 0;
547 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700548
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 // Do some basic type conversions that make sense. uint can be
550 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700551 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700553 v = static_cast<int64_t>(*uintValue);
554 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700556 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700558 d = static_cast<double>(*uintValue);
559 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 d = static_cast<double>(*intValue);
564 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700565 }
566
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700567 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700569 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 {
571 return -1;
572 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000573 r = sd_bus_message_append_basic(m, argCode[0],
574 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 if (r < 0)
576 {
577 return r;
578 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700579 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700580 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700582 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 {
584 return -1;
585 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700586 int32_t i = static_cast<int32_t>(*intValue);
587 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 if (r < 0)
589 {
590 return r;
591 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700592 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 {
595 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700596 int boolInt = false;
597 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 }
601 else if (b != nullptr)
602 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600603 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700605 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700607 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 }
609 else
610 {
611 return -1;
612 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700613 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 if (r < 0)
615 {
616 return r;
617 }
618 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700619 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700621 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 {
623 return -1;
624 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 int16_t n = static_cast<int16_t>(*intValue);
626 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 if (r < 0)
628 {
629 return r;
630 }
631 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700632 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 {
636 return -1;
637 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700638 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 if (r < 0)
640 {
641 return r;
642 }
643 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700644 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700646 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 {
648 return -1;
649 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700650 uint8_t y = static_cast<uint8_t>(*uintValue);
651 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700653 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
657 return -1;
658 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700659 uint16_t q = static_cast<uint16_t>(*uintValue);
660 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700662 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
666 return -1;
667 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700668 uint32_t u = static_cast<uint32_t>(*uintValue);
669 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700670 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
675 return -1;
676 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700677 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700681 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700683 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700685 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700687 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 if (r < 0)
689 {
690 return r;
691 }
692
693 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
694 ++it)
695 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700696 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 if (r < 0)
698 {
699 return r;
700 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 }
702 sd_bus_message_close_container(m);
703 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 std::string containedType = argCode.substr(1);
707 BMCWEB_LOG_DEBUG << "variant type: " << argCode
708 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700710 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 if (r < 0)
712 {
713 return r;
714 }
715
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700716 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700717 if (r < 0)
718 {
719 return r;
720 }
721
722 r = sd_bus_message_close_container(m);
723 if (r < 0)
724 {
725 return r;
726 }
727 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700728 else if (boost::starts_with(argCode, "(") &&
729 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700731 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700733 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800734 if (r < 0)
735 {
736 return r;
737 }
738
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000740 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 {
742 if (it == j->end())
743 {
744 return -1;
745 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000746 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 if (r < 0)
748 {
749 return r;
750 }
751 it++;
752 }
753 r = sd_bus_message_close_container(m);
754 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700755 else if (boost::starts_with(argCode, "{") &&
756 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700758 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700760 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800761 if (r < 0)
762 {
763 return r;
764 }
765
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700766 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 if (codes.size() != 2)
768 {
769 return -1;
770 }
771 const std::string &key_type = codes[0];
772 const std::string &value_type = codes[1];
773 for (auto it : j->items())
774 {
775 r = convertJsonToDbus(m, key_type, it.key());
776 if (r < 0)
777 {
778 return r;
779 }
780
781 r = convertJsonToDbus(m, value_type, it.value());
782 if (r < 0)
783 {
784 return r;
785 }
786 }
787 r = sd_bus_message_close_container(m);
788 }
789 else
790 {
791 return -2;
792 }
793 if (r < 0)
794 {
795 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700796 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700797
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 if (argTypes.size() > 1)
799 {
800 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700801 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700802 }
Matt Spinler127ea542019-01-14 11:04:28 -0600803
804 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700805}
806
Matt Spinlerd22a7132019-01-14 12:14:30 -0600807template <typename T>
808int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
809 nlohmann::json &data)
810{
811 T value;
812
813 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
814 if (r < 0)
815 {
816 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
817 << " failed!";
818 return r;
819 }
820
821 data = value;
822 return 0;
823}
824
Matt Spinler16caaee2019-01-15 11:40:34 -0600825int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600826 sdbusplus::message::message &m, nlohmann::json &response);
827
828int readDictEntryFromMessage(const std::string &typeCode,
829 sdbusplus::message::message &m,
830 nlohmann::json &object)
831{
832 std::vector<std::string> types = dbusArgSplit(typeCode);
833 if (types.size() != 2)
834 {
835 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
836 << types.size();
837 return -1;
838 }
839
840 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
841 typeCode.c_str());
842 if (r < 0)
843 {
844 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
845 return r;
846 }
847
848 nlohmann::json key;
849 r = convertDBusToJSON(types[0], m, key);
850 if (r < 0)
851 {
852 return r;
853 }
854
855 const std::string *keyPtr = key.get_ptr<const std::string *>();
856 if (keyPtr == nullptr)
857 {
858 // json doesn't support non-string keys. If we hit this condition,
859 // convert the result to a string so we can proceed
860 key = key.dump();
861 keyPtr = key.get_ptr<const std::string *>();
862 // in theory this can't fail now, but lets be paranoid about it anyway
863 if (keyPtr == nullptr)
864 {
865 return -1;
866 }
867 }
868 nlohmann::json &value = object[*keyPtr];
869
870 r = convertDBusToJSON(types[1], m, value);
871 if (r < 0)
872 {
873 return r;
874 }
875
876 r = sd_bus_message_exit_container(m.get());
877 if (r < 0)
878 {
879 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
880 return r;
881 }
882
883 return 0;
884}
885
886int readArrayFromMessage(const std::string &typeCode,
887 sdbusplus::message::message &m, nlohmann::json &data)
888{
889 if (typeCode.size() < 2)
890 {
891 BMCWEB_LOG_ERROR << "Type code " << typeCode
892 << " too small for an array";
893 return -1;
894 }
895
896 std::string containedType = typeCode.substr(1);
897
898 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
899 containedType.c_str());
900 if (r < 0)
901 {
902 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
903 << r;
904 return r;
905 }
906
907 bool dict = boost::starts_with(containedType, "{") &&
908 boost::ends_with(containedType, "}");
909
910 if (dict)
911 {
912 // Remove the { }
913 containedType = containedType.substr(1, containedType.size() - 2);
914 data = nlohmann::json::object();
915 }
916 else
917 {
918 data = nlohmann::json::array();
919 }
920
921 while (true)
922 {
923 r = sd_bus_message_at_end(m.get(), false);
924 if (r < 0)
925 {
926 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
927 return r;
928 }
929
930 if (r > 0)
931 {
932 break;
933 }
934
935 // Dictionaries are only ever seen in an array
936 if (dict)
937 {
938 r = readDictEntryFromMessage(containedType, m, data);
939 if (r < 0)
940 {
941 return r;
942 }
943 }
944 else
945 {
946 data.push_back(nlohmann::json());
947
948 r = convertDBusToJSON(containedType, m, data.back());
949 if (r < 0)
950 {
951 return r;
952 }
953 }
954 }
955
956 r = sd_bus_message_exit_container(m.get());
957 if (r < 0)
958 {
959 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
960 return r;
961 }
962
963 return 0;
964}
965
Matt Spinler75c6c672019-01-14 13:01:46 -0600966int readStructFromMessage(const std::string &typeCode,
967 sdbusplus::message::message &m, nlohmann::json &data)
968{
969 if (typeCode.size() < 3)
970 {
971 BMCWEB_LOG_ERROR << "Type code " << typeCode
972 << " too small for a struct";
973 return -1;
974 }
975
976 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
977 std::vector<std::string> types = dbusArgSplit(containedTypes);
978
979 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
980 containedTypes.c_str());
981 if (r < 0)
982 {
983 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
984 << r;
985 return r;
986 }
987
988 for (const std::string &type : types)
989 {
990 data.push_back(nlohmann::json());
991 r = convertDBusToJSON(type, m, data.back());
992 if (r < 0)
993 {
994 return r;
995 }
996 }
997
998 r = sd_bus_message_exit_container(m.get());
999 if (r < 0)
1000 {
1001 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1002 return r;
1003 }
1004 return 0;
1005}
1006
Matt Spinler89c19702019-01-14 13:13:00 -06001007int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1008{
1009 const char *containerType;
1010 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1011 if (r < 0)
1012 {
1013 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1014 return r;
1015 }
1016
1017 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1018 containerType);
1019 if (r < 0)
1020 {
1021 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1022 << r;
1023 return r;
1024 }
1025
1026 r = convertDBusToJSON(containerType, m, data);
1027 if (r < 0)
1028 {
1029 return r;
1030 }
1031
1032 r = sd_bus_message_exit_container(m.get());
1033 if (r < 0)
1034 {
1035 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1036 return r;
1037 }
1038
1039 return 0;
1040}
1041
Matt Spinler6df8f992019-01-14 12:47:47 -06001042int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001043 sdbusplus::message::message &m, nlohmann::json &response)
1044{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001045 int r = 0;
1046 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1047
Matt Spinlerd22a7132019-01-14 12:14:30 -06001048 for (const std::string &typeCode : returnTypes)
1049 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001050 nlohmann::json *thisElement = &response;
1051 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001052 {
1053 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001054 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001055 }
1056
1057 if (typeCode == "s")
1058 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001059 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001060 if (r < 0)
1061 {
1062 return r;
1063 }
1064 }
1065 else if (typeCode == "g")
1066 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001067 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001068 if (r < 0)
1069 {
1070 return r;
1071 }
1072 }
1073 else if (typeCode == "o")
1074 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001075 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001076 if (r < 0)
1077 {
1078 return r;
1079 }
1080 }
1081 else if (typeCode == "b")
1082 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001083 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001084 if (r < 0)
1085 {
1086 return r;
1087 }
1088
Matt Spinlerf39420c2019-01-30 12:57:18 -06001089 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001090 }
1091 else if (typeCode == "u")
1092 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001093 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001094 if (r < 0)
1095 {
1096 return r;
1097 }
1098 }
1099 else if (typeCode == "i")
1100 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001101 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001102 if (r < 0)
1103 {
1104 return r;
1105 }
1106 }
1107 else if (typeCode == "x")
1108 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001109 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 if (r < 0)
1111 {
1112 return r;
1113 }
1114 }
1115 else if (typeCode == "t")
1116 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001117 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001118 if (r < 0)
1119 {
1120 return r;
1121 }
1122 }
1123 else if (typeCode == "n")
1124 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001125 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001126 if (r < 0)
1127 {
1128 return r;
1129 }
1130 }
1131 else if (typeCode == "q")
1132 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001133 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001134 if (r < 0)
1135 {
1136 return r;
1137 }
1138 }
1139 else if (typeCode == "y")
1140 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001141 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001142 if (r < 0)
1143 {
1144 return r;
1145 }
1146 }
1147 else if (typeCode == "d")
1148 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001149 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001150 if (r < 0)
1151 {
1152 return r;
1153 }
1154 }
1155 else if (typeCode == "h")
1156 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001157 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001158 if (r < 0)
1159 {
1160 return r;
1161 }
1162 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001163 else if (boost::starts_with(typeCode, "a"))
1164 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001165 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001166 if (r < 0)
1167 {
1168 return r;
1169 }
1170 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001171 else if (boost::starts_with(typeCode, "(") &&
1172 boost::ends_with(typeCode, ")"))
1173 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001174 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001175 if (r < 0)
1176 {
1177 return r;
1178 }
1179 }
Matt Spinler89c19702019-01-14 13:13:00 -06001180 else if (boost::starts_with(typeCode, "v"))
1181 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001182 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001183 if (r < 0)
1184 {
1185 return r;
1186 }
1187 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001188 else
1189 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001190 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1191 return -2;
1192 }
1193 }
1194
Matt Spinler16caaee2019-01-15 11:40:34 -06001195 return 0;
1196}
1197
1198void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1199 sdbusplus::message::message &m,
1200 const std::string &returnType)
1201{
Matt Spinler39a4e392019-01-15 11:53:13 -06001202 nlohmann::json data;
1203
1204 int r = convertDBusToJSON(returnType, m, data);
1205 if (r < 0)
1206 {
1207 transaction->outputFailed = true;
1208 return;
1209 }
1210
1211 if (data.is_null())
1212 {
1213 return;
1214 }
1215
1216 if (transaction->methodResponse.is_null())
1217 {
1218 transaction->methodResponse = std::move(data);
1219 return;
1220 }
1221
1222 // If they're both dictionaries or arrays, merge into one.
1223 // Otherwise, make the results an array with every result
1224 // an entry. Could also just fail in that case, but it
1225 // seems better to get the data back somehow.
1226
1227 if (transaction->methodResponse.is_object() && data.is_object())
1228 {
1229 for (const auto &obj : data.items())
1230 {
1231 // Note: Will overwrite the data for a duplicate key
1232 transaction->methodResponse.emplace(obj.key(),
1233 std::move(obj.value()));
1234 }
1235 return;
1236 }
1237
1238 if (transaction->methodResponse.is_array() && data.is_array())
1239 {
1240 for (auto &obj : data)
1241 {
1242 transaction->methodResponse.push_back(std::move(obj));
1243 }
1244 return;
1245 }
1246
1247 if (!transaction->convertedToArray)
1248 {
1249 // They are different types. May as well turn them into an array
1250 nlohmann::json j = std::move(transaction->methodResponse);
1251 transaction->methodResponse = nlohmann::json::array();
1252 transaction->methodResponse.push_back(std::move(j));
1253 transaction->methodResponse.push_back(std::move(data));
1254 transaction->convertedToArray = true;
1255 }
1256 else
1257 {
1258 transaction->methodResponse.push_back(std::move(data));
1259 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001260}
1261
Ed Tanousd76323e2018-08-07 14:35:40 -07001262void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001263 const std::string &connectionName)
1264{
1265 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1266 << connectionName;
1267 crow::connections::systemBus->async_method_call(
1268 [transaction, connectionName{std::string(connectionName)}](
1269 const boost::system::error_code ec,
1270 const std::string &introspect_xml) {
1271 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1272 if (ec)
1273 {
1274 BMCWEB_LOG_ERROR
1275 << "Introspect call failed with error: " << ec.message()
1276 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001277 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001278 }
Matt Spinler318bd892019-01-15 09:59:20 -06001279 tinyxml2::XMLDocument doc;
1280
1281 doc.Parse(introspect_xml.data(), introspect_xml.size());
1282 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1283 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001284 {
Matt Spinler318bd892019-01-15 09:59:20 -06001285 BMCWEB_LOG_ERROR << "XML document failed to parse "
1286 << connectionName << "\n";
1287 return;
1288 }
1289 tinyxml2::XMLElement *interfaceNode =
1290 pRoot->FirstChildElement("interface");
1291 while (interfaceNode != nullptr)
1292 {
1293 const char *thisInterfaceName =
1294 interfaceNode->Attribute("name");
1295 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001296 {
Matt Spinler318bd892019-01-15 09:59:20 -06001297 if (!transaction->interfaceName.empty() &&
1298 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001299 {
Matt Spinler318bd892019-01-15 09:59:20 -06001300 interfaceNode =
1301 interfaceNode->NextSiblingElement("interface");
1302 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001303 }
Matt Spinler318bd892019-01-15 09:59:20 -06001304
1305 tinyxml2::XMLElement *methodNode =
1306 interfaceNode->FirstChildElement("method");
1307 while (methodNode != nullptr)
1308 {
1309 const char *thisMethodName =
1310 methodNode->Attribute("name");
1311 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1312 if (thisMethodName != nullptr &&
1313 thisMethodName == transaction->methodName)
1314 {
1315 BMCWEB_LOG_DEBUG
1316 << "Found method named " << thisMethodName
1317 << " on interface " << thisInterfaceName;
1318 sdbusplus::message::message m =
1319 crow::connections::systemBus->new_method_call(
1320 connectionName.c_str(),
1321 transaction->path.c_str(),
1322 thisInterfaceName,
1323 transaction->methodName.c_str());
1324
1325 tinyxml2::XMLElement *argumentNode =
1326 methodNode->FirstChildElement("arg");
1327
Matt Spinler16caaee2019-01-15 11:40:34 -06001328 std::string returnType;
1329
1330 // Find the output type
1331 while (argumentNode != nullptr)
1332 {
1333 const char *argDirection =
1334 argumentNode->Attribute("direction");
1335 const char *argType =
1336 argumentNode->Attribute("type");
1337 if (argDirection != nullptr &&
1338 argType != nullptr &&
1339 std::string(argDirection) == "out")
1340 {
1341 returnType = argType;
1342 break;
1343 }
1344 argumentNode =
1345 argumentNode->NextSiblingElement("arg");
1346 }
1347
Matt Spinler318bd892019-01-15 09:59:20 -06001348 nlohmann::json::const_iterator argIt =
1349 transaction->arguments.begin();
1350
Matt Spinler16caaee2019-01-15 11:40:34 -06001351 argumentNode = methodNode->FirstChildElement("arg");
1352
Matt Spinler318bd892019-01-15 09:59:20 -06001353 while (argumentNode != nullptr)
1354 {
1355 const char *argDirection =
1356 argumentNode->Attribute("direction");
1357 const char *argType =
1358 argumentNode->Attribute("type");
1359 if (argDirection != nullptr &&
1360 argType != nullptr &&
1361 std::string(argDirection) == "in")
1362 {
1363 if (argIt == transaction->arguments.end())
1364 {
1365 transaction->setErrorStatus(
1366 "Invalid method args");
1367 return;
1368 }
1369 if (convertJsonToDbus(m.get(),
1370 std::string(argType),
1371 *argIt) < 0)
1372 {
1373 transaction->setErrorStatus(
1374 "Invalid method arg type");
1375 return;
1376 }
1377
1378 argIt++;
1379 }
1380 argumentNode =
1381 argumentNode->NextSiblingElement("arg");
1382 }
1383
1384 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001385 m, [transaction, returnType](
1386 boost::system::error_code ec,
1387 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001388 if (ec)
1389 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001390 transaction->methodFailed = true;
Matt Spinler318bd892019-01-15 09:59:20 -06001391 return;
1392 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001393 else
1394 {
1395 transaction->methodPassed = true;
1396 }
1397
1398 handleMethodResponse(transaction, m,
1399 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001400 });
1401 break;
1402 }
1403 methodNode = methodNode->NextSiblingElement("method");
1404 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001405 }
Matt Spinler318bd892019-01-15 09:59:20 -06001406 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001407 }
1408 },
1409 connectionName, transaction->path,
1410 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001411}
1412
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001413void handleAction(const crow::Request &req, crow::Response &res,
1414 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001415{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001416 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1417 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001418 nlohmann::json requestDbusData =
1419 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001420
Ed Tanous1abe55e2018-09-05 08:30:59 -07001421 if (requestDbusData.is_discarded())
1422 {
Matt Spinler6db06242018-12-11 11:21:22 -06001423 setErrorResponse(res, boost::beast::http::status::bad_request,
1424 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001425 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001426 return;
1427 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001428 nlohmann::json::iterator data = requestDbusData.find("data");
1429 if (data == requestDbusData.end())
1430 {
Matt Spinler6db06242018-12-11 11:21:22 -06001431 setErrorResponse(res, boost::beast::http::status::bad_request,
1432 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001433 res.end();
1434 return;
1435 }
1436
1437 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001438 {
Matt Spinler6db06242018-12-11 11:21:22 -06001439 setErrorResponse(res, boost::beast::http::status::bad_request,
1440 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001441 res.end();
1442 return;
1443 }
1444 auto transaction = std::make_shared<InProgressActionData>(res);
1445
1446 transaction->path = objectPath;
1447 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001448 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001449 crow::connections::systemBus->async_method_call(
1450 [transaction](
1451 const boost::system::error_code ec,
1452 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001453 &interfaceNames) {
1454 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001455 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001456 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001457 setErrorResponse(transaction->res,
1458 boost::beast::http::status::not_found,
1459 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001460 return;
1461 }
1462
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001463 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1464 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001465
1466 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001467 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001468 {
1469 findActionOnInterface(transaction, object.first);
1470 }
1471 },
1472 "xyz.openbmc_project.ObjectMapper",
1473 "/xyz/openbmc_project/object_mapper",
1474 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1475 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001476}
1477
Matt Spinlerde818812018-12-11 16:39:20 -06001478void handleDelete(const crow::Request &req, crow::Response &res,
1479 const std::string &objectPath)
1480{
1481 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1482
1483 crow::connections::systemBus->async_method_call(
1484 [&res, objectPath](
1485 const boost::system::error_code ec,
1486 const std::vector<std::pair<std::string, std::vector<std::string>>>
1487 &interfaceNames) {
1488 if (ec || interfaceNames.size() <= 0)
1489 {
1490 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001491 setErrorResponse(res,
1492 boost::beast::http::status::method_not_allowed,
1493 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001494 res.end();
1495 return;
1496 }
1497
1498 auto transaction = std::make_shared<InProgressActionData>(res);
1499 transaction->path = objectPath;
1500 transaction->methodName = "Delete";
1501 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1502
1503 for (const std::pair<std::string, std::vector<std::string>>
1504 &object : interfaceNames)
1505 {
1506 findActionOnInterface(transaction, object.first);
1507 }
1508 },
1509 "xyz.openbmc_project.ObjectMapper",
1510 "/xyz/openbmc_project/object_mapper",
1511 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1512 std::array<const char *, 0>());
1513}
1514
Ed Tanousf839dfe2018-11-12 11:11:15 -08001515void handleList(crow::Response &res, const std::string &objectPath,
1516 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517{
1518 crow::connections::systemBus->async_method_call(
1519 [&res](const boost::system::error_code ec,
1520 std::vector<std::string> &objectPaths) {
1521 if (ec)
1522 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001523 setErrorResponse(res, boost::beast::http::status::not_found,
1524 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001525 }
1526 else
1527 {
1528 res.jsonValue = {{"status", "ok"},
1529 {"message", "200 OK"},
1530 {"data", std::move(objectPaths)}};
1531 }
1532 res.end();
1533 },
1534 "xyz.openbmc_project.ObjectMapper",
1535 "/xyz/openbmc_project/object_mapper",
1536 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001537 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001538}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001539
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001540void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001541{
Ed Tanous049a0512018-11-01 13:58:42 -07001542 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1543 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1544
1545 asyncResp->res.jsonValue = {{"message", "200 OK"},
1546 {"status", "ok"},
1547 {"data", nlohmann::json::object()}};
1548
Ed Tanous1abe55e2018-09-05 08:30:59 -07001549 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001550 [objectPath, asyncResp](const boost::system::error_code ec,
1551 GetSubTreeType &object_names) {
1552 auto transaction = std::make_shared<InProgressEnumerateData>(
1553 objectPath, asyncResp);
1554
1555 transaction->subtree =
1556 std::make_shared<GetSubTreeType>(std::move(object_names));
1557
Ed Tanous1abe55e2018-09-05 08:30:59 -07001558 if (ec)
1559 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001560 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1561 << transaction->objectPath;
1562 setErrorResponse(transaction->asyncResp->res,
1563 boost::beast::http::status::not_found,
1564 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001565 return;
1566 }
Ed Tanous64530012018-02-06 17:08:16 -08001567
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001568 // Add the data for the path passed in to the results
1569 // as if GetSubTree returned it, and continue on enumerating
1570 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001571 },
1572 "xyz.openbmc_project.ObjectMapper",
1573 "/xyz/openbmc_project/object_mapper",
1574 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001575 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001576}
Ed Tanous911ac312017-08-15 09:37:42 -07001577
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001578void handleGet(crow::Response &res, std::string &objectPath,
1579 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001580{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001581 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1582 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001583 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001584
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585 std::shared_ptr<std::string> path =
1586 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001587
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588 using GetObjectType =
1589 std::vector<std::pair<std::string, std::vector<std::string>>>;
1590 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001591 [&res, path, propertyName](const boost::system::error_code ec,
1592 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001593 if (ec || object_names.size() <= 0)
1594 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001595 setErrorResponse(res, boost::beast::http::status::not_found,
1596 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001597 res.end();
1598 return;
1599 }
1600 std::shared_ptr<nlohmann::json> response =
1601 std::make_shared<nlohmann::json>(nlohmann::json::object());
1602 // The mapper should never give us an empty interface names list,
1603 // but check anyway
1604 for (const std::pair<std::string, std::vector<std::string>>
1605 connection : object_names)
1606 {
1607 const std::vector<std::string> &interfaceNames =
1608 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001609
Ed Tanous1abe55e2018-09-05 08:30:59 -07001610 if (interfaceNames.size() <= 0)
1611 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001612 setErrorResponse(res, boost::beast::http::status::not_found,
1613 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001614 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001615 return;
1616 }
1617
1618 for (const std::string &interface : interfaceNames)
1619 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001620 sdbusplus::message::message m =
1621 crow::connections::systemBus->new_method_call(
1622 connection.first.c_str(), path->c_str(),
1623 "org.freedesktop.DBus.Properties", "GetAll");
1624 m.append(interface);
1625 crow::connections::systemBus->async_send(
1626 m, [&res, response,
1627 propertyName](const boost::system::error_code ec,
1628 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001629 if (ec)
1630 {
1631 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1632 << ec;
1633 }
1634 else
1635 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001636 nlohmann::json properties;
1637 int r =
1638 convertDBusToJSON("a{sv}", msg, properties);
1639 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001640 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001641 BMCWEB_LOG_ERROR
1642 << "convertDBusToJSON failed";
1643 }
1644 else
1645 {
1646 for (auto &prop : properties.items())
1647 {
1648 // if property name is empty, or matches
1649 // our search query, add it to the
1650 // response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001651
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001652 if (propertyName->empty())
1653 {
1654 (*response)[prop.key()] =
1655 std::move(prop.value());
1656 }
1657 else if (prop.key() == *propertyName)
1658 {
1659 *response = std::move(prop.value());
1660 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001661 }
1662 }
1663 }
1664 if (response.use_count() == 1)
1665 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001666 if (!propertyName->empty() && response->empty())
1667 {
1668 setErrorResponse(
1669 res,
1670 boost::beast::http::status::not_found,
1671 propNotFoundDesc, notFoundMsg);
1672 }
1673 else
1674 {
1675 res.jsonValue = {{"status", "ok"},
1676 {"message", "200 OK"},
1677 {"data", *response}};
1678 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001679 res.end();
1680 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001681 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001682 }
1683 }
1684 },
1685 "xyz.openbmc_project.ObjectMapper",
1686 "/xyz/openbmc_project/object_mapper",
1687 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1688 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001689}
1690
Ed Tanous1abe55e2018-09-05 08:30:59 -07001691struct AsyncPutRequest
1692{
1693 AsyncPutRequest(crow::Response &res) : res(res)
1694 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001695 }
1696 ~AsyncPutRequest()
1697 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 if (res.jsonValue.empty())
1699 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001700 setErrorResponse(res, boost::beast::http::status::forbidden,
1701 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001702 }
1703
1704 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001705 }
1706
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001707 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001708 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001709 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1710 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001711 }
1712
Ed Tanous1abe55e2018-09-05 08:30:59 -07001713 crow::Response &res;
1714 std::string objectPath;
1715 std::string propertyName;
1716 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001717};
1718
Ed Tanousd76323e2018-08-07 14:35:40 -07001719void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001720 const std::string &objectPath, const std::string &destProperty)
1721{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001722 if (destProperty.empty())
1723 {
1724 setErrorResponse(res, boost::beast::http::status::forbidden,
1725 forbiddenResDesc, forbiddenMsg);
1726 res.end();
1727 return;
1728 }
1729
Ed Tanous1abe55e2018-09-05 08:30:59 -07001730 nlohmann::json requestDbusData =
1731 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001732
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 if (requestDbusData.is_discarded())
1734 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001735 setErrorResponse(res, boost::beast::http::status::bad_request,
1736 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001737 res.end();
1738 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001739 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001740
Ed Tanous1abe55e2018-09-05 08:30:59 -07001741 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1742 if (propertyIt == requestDbusData.end())
1743 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001744 setErrorResponse(res, boost::beast::http::status::bad_request,
1745 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 res.end();
1747 return;
1748 }
1749 const nlohmann::json &propertySetValue = *propertyIt;
1750 auto transaction = std::make_shared<AsyncPutRequest>(res);
1751 transaction->objectPath = objectPath;
1752 transaction->propertyName = destProperty;
1753 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001754
Ed Tanous1abe55e2018-09-05 08:30:59 -07001755 using GetObjectType =
1756 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001757
Ed Tanous1abe55e2018-09-05 08:30:59 -07001758 crow::connections::systemBus->async_method_call(
1759 [transaction](const boost::system::error_code ec,
1760 const GetObjectType &object_names) {
1761 if (!ec && object_names.size() <= 0)
1762 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001763 setErrorResponse(transaction->res,
1764 boost::beast::http::status::not_found,
1765 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001766 return;
1767 }
Ed Tanous911ac312017-08-15 09:37:42 -07001768
Ed Tanous1abe55e2018-09-05 08:30:59 -07001769 for (const std::pair<std::string, std::vector<std::string>>
1770 connection : object_names)
1771 {
1772 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001773
Ed Tanous1abe55e2018-09-05 08:30:59 -07001774 crow::connections::systemBus->async_method_call(
1775 [connectionName{std::string(connectionName)},
1776 transaction](const boost::system::error_code ec,
1777 const std::string &introspectXml) {
1778 if (ec)
1779 {
1780 BMCWEB_LOG_ERROR
1781 << "Introspect call failed with error: "
1782 << ec.message()
1783 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001784 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001785 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001786 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001787 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001788
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 doc.Parse(introspectXml.c_str());
1790 tinyxml2::XMLNode *pRoot =
1791 doc.FirstChildElement("node");
1792 if (pRoot == nullptr)
1793 {
1794 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1795 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001796 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001797 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001798 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001799 tinyxml2::XMLElement *ifaceNode =
1800 pRoot->FirstChildElement("interface");
1801 while (ifaceNode != nullptr)
1802 {
1803 const char *interfaceName =
1804 ifaceNode->Attribute("name");
1805 BMCWEB_LOG_DEBUG << "found interface "
1806 << interfaceName;
1807 tinyxml2::XMLElement *propNode =
1808 ifaceNode->FirstChildElement("property");
1809 while (propNode != nullptr)
1810 {
1811 const char *propertyName =
1812 propNode->Attribute("name");
1813 BMCWEB_LOG_DEBUG << "Found property "
1814 << propertyName;
1815 if (propertyName == transaction->propertyName)
1816 {
1817 const char *argType =
1818 propNode->Attribute("type");
1819 if (argType != nullptr)
1820 {
1821 sdbusplus::message::message m =
1822 crow::connections::systemBus
1823 ->new_method_call(
1824 connectionName.c_str(),
1825 transaction->objectPath
1826 .c_str(),
1827 "org.freedesktop.DBus."
1828 "Properties",
1829 "Set");
1830 m.append(interfaceName,
1831 transaction->propertyName);
1832 int r = sd_bus_message_open_container(
1833 m.get(), SD_BUS_TYPE_VARIANT,
1834 argType);
1835 if (r < 0)
1836 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001837 transaction->setErrorStatus(
1838 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001839 return;
1840 }
1841 r = convertJsonToDbus(
1842 m.get(), argType,
1843 transaction->propertyValue);
1844 if (r < 0)
1845 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001846 transaction->setErrorStatus(
1847 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001848 return;
1849 }
1850 r = sd_bus_message_close_container(
1851 m.get());
1852 if (r < 0)
1853 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001854 transaction->setErrorStatus(
1855 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001856 return;
1857 }
Ed Tanous911ac312017-08-15 09:37:42 -07001858
Ed Tanous1abe55e2018-09-05 08:30:59 -07001859 crow::connections::systemBus
1860 ->async_send(
1861 m,
1862 [transaction](
1863 boost::system::error_code
1864 ec,
1865 sdbusplus::message::message
1866 &m) {
1867 BMCWEB_LOG_DEBUG << "sent";
1868 if (ec)
1869 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001870 setErrorResponse(
1871 transaction->res,
1872 boost::beast::http::
1873 status::
1874 forbidden,
1875 forbiddenPropDesc,
1876 ec.message());
1877 }
1878 else
1879 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001880 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001881 .jsonValue = {
1882 {"status", "ok"},
1883 {"message",
1884 "200 OK"},
1885 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001886 }
1887 });
1888 }
1889 }
1890 propNode =
1891 propNode->NextSiblingElement("property");
1892 }
1893 ifaceNode =
1894 ifaceNode->NextSiblingElement("interface");
1895 }
1896 },
1897 connectionName, transaction->objectPath,
1898 "org.freedesktop.DBus.Introspectable", "Introspect");
1899 }
1900 },
1901 "xyz.openbmc_project.ObjectMapper",
1902 "/xyz/openbmc_project/object_mapper",
1903 "xyz.openbmc_project.ObjectMapper", "GetObject",
1904 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001905}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001906
Ed Tanous049a0512018-11-01 13:58:42 -07001907inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1908 std::string &objectPath)
1909{
Ed Tanous049a0512018-11-01 13:58:42 -07001910
1911 // If accessing a single attribute, fill in and update objectPath,
1912 // otherwise leave destProperty blank
1913 std::string destProperty = "";
1914 const char *attrSeperator = "/attr/";
1915 size_t attrPosition = objectPath.find(attrSeperator);
1916 if (attrPosition != objectPath.npos)
1917 {
1918 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1919 objectPath.length());
1920 objectPath = objectPath.substr(0, attrPosition);
1921 }
1922
1923 if (req.method() == "POST"_method)
1924 {
1925 constexpr const char *actionSeperator = "/action/";
1926 size_t actionPosition = objectPath.find(actionSeperator);
1927 if (actionPosition != objectPath.npos)
1928 {
1929 std::string postProperty =
1930 objectPath.substr((actionPosition + strlen(actionSeperator)),
1931 objectPath.length());
1932 objectPath = objectPath.substr(0, actionPosition);
1933 handleAction(req, res, objectPath, postProperty);
1934 return;
1935 }
1936 }
1937 else if (req.method() == "GET"_method)
1938 {
1939 if (boost::ends_with(objectPath, "/enumerate"))
1940 {
1941 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1942 objectPath.end());
1943 handleEnumerate(res, objectPath);
1944 }
1945 else if (boost::ends_with(objectPath, "/list"))
1946 {
1947 objectPath.erase(objectPath.end() - sizeof("list"),
1948 objectPath.end());
1949 handleList(res, objectPath);
1950 }
1951 else
1952 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001953 // Trim any trailing "/" at the end
1954 if (boost::ends_with(objectPath, "/"))
1955 {
1956 objectPath.pop_back();
1957 handleList(res, objectPath, 1);
1958 }
1959 else
1960 {
1961 handleGet(res, objectPath, destProperty);
1962 }
Ed Tanous049a0512018-11-01 13:58:42 -07001963 }
1964 return;
1965 }
1966 else if (req.method() == "PUT"_method)
1967 {
1968 handlePut(req, res, objectPath, destProperty);
1969 return;
1970 }
Matt Spinlerde818812018-12-11 16:39:20 -06001971 else if (req.method() == "DELETE"_method)
1972 {
1973 handleDelete(req, res, objectPath);
1974 return;
1975 }
Ed Tanous049a0512018-11-01 13:58:42 -07001976
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001977 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1978 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001979 res.end();
1980}
1981
Ed Tanous1abe55e2018-09-05 08:30:59 -07001982template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1983{
1984 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03001985 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07001986 .methods("GET"_method)(
1987 [](const crow::Request &req, crow::Response &res) {
1988 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1989 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001990 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001991 });
1992
1993 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03001994 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07001995 .methods("GET"_method)(
1996 [](const crow::Request &req, crow::Response &res) {
1997 auto myCallback = [&res](const boost::system::error_code ec,
1998 std::vector<std::string> &names) {
1999 if (ec)
2000 {
2001 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2002 res.result(
2003 boost::beast::http::status::internal_server_error);
2004 }
2005 else
2006 {
2007 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002008 res.jsonValue = {{"status", "ok"}};
2009 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002010 for (auto &name : names)
2011 {
2012 objectsSub.push_back({{"name", name}});
2013 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002014 }
2015 res.end();
2016 };
2017 crow::connections::systemBus->async_method_call(
2018 std::move(myCallback), "org.freedesktop.DBus", "/",
2019 "org.freedesktop.DBus", "ListNames");
2020 });
2021
2022 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002023 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002024 .methods("GET"_method)(
2025 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002026 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002027 });
2028
2029 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002030 .requires({"Login"})
2031 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2032 const std::string &path) {
2033 std::string objectPath = "/xyz/" + path;
2034 handleDBusUrl(req, res, objectPath);
2035 });
2036
2037 BMCWEB_ROUTE(app, "/xyz/<path>")
2038 .requires({"ConfigureComponents", "ConfigureManager"})
2039 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002040 [](const crow::Request &req, crow::Response &res,
2041 const std::string &path) {
2042 std::string objectPath = "/xyz/" + path;
2043 handleDBusUrl(req, res, objectPath);
2044 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002045
Ed Tanous049a0512018-11-01 13:58:42 -07002046 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002047 .requires({"Login"})
2048 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2049 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002050 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002051 handleDBusUrl(req, res, objectPath);
2052 });
2053
2054 BMCWEB_ROUTE(app, "/org/<path>")
2055 .requires({"ConfigureComponents", "ConfigureManager"})
2056 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002057 [](const crow::Request &req, crow::Response &res,
2058 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002059 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002060 handleDBusUrl(req, res, objectPath);
2061 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002062
Ed Tanous1abe55e2018-09-05 08:30:59 -07002063 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002064 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002065 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2066 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002067 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002068 if (!std::regex_match(dumpId, validFilename))
2069 {
Ed Tanousad18f072018-11-14 14:07:48 -08002070 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002071 res.end();
2072 return;
2073 }
James Feistf6150402019-01-08 10:36:20 -08002074 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002075 "/var/lib/phosphor-debug-collector/dumps");
2076
Ed Tanousad18f072018-11-14 14:07:48 -08002077 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002078
James Feistf6150402019-01-08 10:36:20 -08002079 if (!std::filesystem::exists(loc) ||
2080 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002081 {
Ed Tanousad18f072018-11-14 14:07:48 -08002082 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002083 res.result(boost::beast::http::status::not_found);
2084 res.end();
2085 return;
2086 }
James Feistf6150402019-01-08 10:36:20 -08002087 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002088
Ed Tanous1abe55e2018-09-05 08:30:59 -07002089 for (auto &file : files)
2090 {
2091 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002092 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002093 {
2094 continue;
2095 }
2096 res.addHeader("Content-Type", "application/octet-stream");
2097 res.body() = {std::istreambuf_iterator<char>(readFile),
2098 std::istreambuf_iterator<char>()};
2099 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002100 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002101 }
2102 res.result(boost::beast::http::status::not_found);
2103 res.end();
2104 return;
2105 });
2106
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002107 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002108 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002109 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002110 const std::string &Connection) {
2111 introspectObjects(Connection, "/",
2112 std::make_shared<bmcweb::AsyncResp>(res));
2113 });
2114
2115 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2116 .methods("GET"_method,
2117 "POST"_method)([](const crow::Request &req,
2118 crow::Response &res,
2119 const std::string &processName,
2120 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002121 std::vector<std::string> strs;
2122 boost::split(strs, requestedPath, boost::is_any_of("/"));
2123 std::string objectPath;
2124 std::string interfaceName;
2125 std::string methodName;
2126 auto it = strs.begin();
2127 if (it == strs.end())
2128 {
2129 objectPath = "/";
2130 }
2131 while (it != strs.end())
2132 {
2133 // Check if segment contains ".". If it does, it must be an
2134 // interface
2135 if (it->find(".") != std::string::npos)
2136 {
2137 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002138 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07002139 // as part of our <path> specifier above, which causes the
2140 // normal trailing backslash redirector to fail.
2141 }
2142 else if (!it->empty())
2143 {
2144 objectPath += "/" + *it;
2145 }
2146 it++;
2147 }
2148 if (it != strs.end())
2149 {
2150 interfaceName = *it;
2151 it++;
2152
2153 // after interface, we might have a method name
2154 if (it != strs.end())
2155 {
2156 methodName = *it;
2157 it++;
2158 }
2159 }
2160 if (it != strs.end())
2161 {
2162 // if there is more levels past the method name, something went
2163 // wrong, return not found
2164 res.result(boost::beast::http::status::not_found);
2165 res.end();
2166 return;
2167 }
2168 if (interfaceName.empty())
2169 {
2170 crow::connections::systemBus->async_method_call(
2171 [&, processName,
2172 objectPath](const boost::system::error_code ec,
2173 const std::string &introspect_xml) {
2174 if (ec)
2175 {
2176 BMCWEB_LOG_ERROR
2177 << "Introspect call failed with error: "
2178 << ec.message()
2179 << " on process: " << processName
2180 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002181 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002182 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002183 tinyxml2::XMLDocument doc;
2184
2185 doc.Parse(introspect_xml.c_str());
2186 tinyxml2::XMLNode *pRoot =
2187 doc.FirstChildElement("node");
2188 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002189 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002190 BMCWEB_LOG_ERROR << "XML document failed to parse "
2191 << processName << " " << objectPath
2192 << "\n";
2193 res.jsonValue = {{"status", "XML parse error"}};
2194 res.result(boost::beast::http::status::
2195 internal_server_error);
2196 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002197 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002198
2199 BMCWEB_LOG_DEBUG << introspect_xml;
2200 res.jsonValue = {{"status", "ok"},
2201 {"bus_name", processName},
2202 {"object_path", objectPath}};
2203 nlohmann::json &interfacesArray =
2204 res.jsonValue["interfaces"];
2205 interfacesArray = nlohmann::json::array();
2206 tinyxml2::XMLElement *interface =
2207 pRoot->FirstChildElement("interface");
2208
2209 while (interface != nullptr)
2210 {
2211 const char *ifaceName =
2212 interface->Attribute("name");
2213 if (ifaceName != nullptr)
2214 {
2215 interfacesArray.push_back(
2216 {{"name", ifaceName}});
2217 }
2218
2219 interface =
2220 interface->NextSiblingElement("interface");
2221 }
2222
Ed Tanous1abe55e2018-09-05 08:30:59 -07002223 res.end();
2224 },
2225 processName, objectPath,
2226 "org.freedesktop.DBus.Introspectable", "Introspect");
2227 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002228 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002229 {
2230 crow::connections::systemBus->async_method_call(
2231 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002232 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07002233 const boost::system::error_code ec,
2234 const std::string &introspect_xml) {
2235 if (ec)
2236 {
2237 BMCWEB_LOG_ERROR
2238 << "Introspect call failed with error: "
2239 << ec.message()
2240 << " on process: " << processName
2241 << " path: " << objectPath << "\n";
2242 }
2243 else
2244 {
2245 tinyxml2::XMLDocument doc;
2246
2247 doc.Parse(introspect_xml.c_str());
2248 tinyxml2::XMLNode *pRoot =
2249 doc.FirstChildElement("node");
2250 if (pRoot == nullptr)
2251 {
2252 BMCWEB_LOG_ERROR
2253 << "XML document failed to parse "
2254 << processName << " " << objectPath << "\n";
2255 res.result(boost::beast::http::status::
2256 internal_server_error);
2257 }
2258 else
2259 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002260 // if we know we're the only call, build the
2261 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07002262 tinyxml2::XMLElement *interface =
2263 pRoot->FirstChildElement("interface");
2264
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002265 res.jsonValue = {
2266 {"status", "ok"},
2267 {"bus_name", processName},
2268 {"interface", interfaceName},
2269 {"object_path", objectPath},
2270 {"properties", nlohmann::json::object()}};
2271
2272 nlohmann::json &methodsArray =
2273 res.jsonValue["methods"];
2274 methodsArray = nlohmann::json::array();
2275
2276 nlohmann::json &signalsArray =
2277 res.jsonValue["signals"];
2278 signalsArray = nlohmann::json::array();
2279
Ed Tanous1abe55e2018-09-05 08:30:59 -07002280 while (interface != nullptr)
2281 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002282 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002283 interface->Attribute("name");
2284
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002285 if (ifaceName != nullptr &&
2286 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002287 {
2288 tinyxml2::XMLElement *methods =
2289 interface->FirstChildElement(
2290 "method");
2291 while (methods != nullptr)
2292 {
2293 nlohmann::json argsArray =
2294 nlohmann::json::array();
2295 tinyxml2::XMLElement *arg =
2296 methods->FirstChildElement(
2297 "arg");
2298 while (arg != nullptr)
2299 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002300 nlohmann::json thisArg;
2301 for (const char *fieldName :
2302 std::array<const char *,
2303 3>{"name",
2304 "direction",
2305 "type"})
2306 {
2307 const char *fieldValue =
2308 arg->Attribute(
2309 fieldName);
2310 if (fieldValue != nullptr)
2311 {
2312 thisArg[fieldName] =
2313 fieldValue;
2314 }
2315 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002316 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002317 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07002318 arg = arg->NextSiblingElement(
2319 "arg");
2320 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002321
2322 const char *name =
2323 methods->Attribute("name");
2324 if (name != nullptr)
2325 {
2326 methodsArray.push_back(
2327 {{"name", name},
2328 {"uri", "/bus/system/" +
2329 processName +
2330 objectPath +
2331 "/" +
2332 interfaceName +
2333 "/" + name},
2334 {"args", argsArray}});
2335 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002336 methods =
2337 methods->NextSiblingElement(
2338 "method");
2339 }
2340 tinyxml2::XMLElement *signals =
2341 interface->FirstChildElement(
2342 "signal");
2343 while (signals != nullptr)
2344 {
2345 nlohmann::json argsArray =
2346 nlohmann::json::array();
2347
2348 tinyxml2::XMLElement *arg =
2349 signals->FirstChildElement(
2350 "arg");
2351 while (arg != nullptr)
2352 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002353 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002354 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002355 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002356 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002357 if (name != nullptr &&
2358 type != nullptr)
2359 {
2360 argsArray.push_back({
2361 {"name", name},
2362 {"type", type},
2363 });
2364 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002365 arg = arg->NextSiblingElement(
2366 "arg");
2367 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002368 const char *name =
2369 signals->Attribute("name");
2370 if (name != nullptr)
2371 {
2372 signalsArray.push_back(
2373 {{"name", name},
2374 {"args", argsArray}});
2375 }
2376
Ed Tanous1abe55e2018-09-05 08:30:59 -07002377 signals =
2378 signals->NextSiblingElement(
2379 "signal");
2380 }
2381
Ed Tanous1abe55e2018-09-05 08:30:59 -07002382 break;
2383 }
2384
2385 interface = interface->NextSiblingElement(
2386 "interface");
2387 }
2388 if (interface == nullptr)
2389 {
2390 // if we got to the end of the list and
2391 // never found a match, throw 404
2392 res.result(
2393 boost::beast::http::status::not_found);
2394 }
2395 }
2396 }
2397 res.end();
2398 },
2399 processName, objectPath,
2400 "org.freedesktop.DBus.Introspectable", "Introspect");
2401 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002402 else
2403 {
2404 if (req.method() != "POST"_method)
2405 {
2406 res.result(boost::beast::http::status::not_found);
2407 res.end();
2408 return;
2409 }
2410
2411 nlohmann::json requestDbusData =
2412 nlohmann::json::parse(req.body, nullptr, false);
2413
2414 if (requestDbusData.is_discarded())
2415 {
2416 res.result(boost::beast::http::status::bad_request);
2417 res.end();
2418 return;
2419 }
2420 if (!requestDbusData.is_array())
2421 {
2422 res.result(boost::beast::http::status::bad_request);
2423 res.end();
2424 return;
2425 }
2426 auto transaction = std::make_shared<InProgressActionData>(res);
2427
2428 transaction->path = objectPath;
2429 transaction->methodName = methodName;
2430 transaction->arguments = std::move(requestDbusData);
2431
2432 findActionOnInterface(transaction, processName);
2433 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002434 });
2435}
2436} // namespace openbmc_mapper
2437} // namespace crow