blob: 9f282a6f5d8ae92c01b41864317a5a3189bad852 [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];
158 sdbusplus::message::variant_ns::visit(
159 [&propertyJson](auto &&val) { propertyJson = val; }, value);
160 }
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];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700258 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 [&propertyJson](auto &&val) {
260 propertyJson = val;
261 },
262 property.second);
263 }
264 }
265 }
Ed Tanous049a0512018-11-01 13:58:42 -0700266 for (const auto &interface : objectPath.second)
267 {
268 if (interface.first == "org.freedesktop.DBus.ObjectManager")
269 {
270 getManagedObjectsForEnumerate(
271 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600272 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700273 }
274 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 }
276 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700277 connection_name, object_manager_path,
278 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
279}
280
281void findObjectManagerPathForEnumerate(
282 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600283 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284{
Ed Tanous049a0512018-11-01 13:58:42 -0700285 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
286 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600288 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 const boost::system::error_code ec,
290 const boost::container::flat_map<
291 std::string, boost::container::flat_map<
292 std::string, std::vector<std::string>>>
293 &objects) {
294 if (ec)
295 {
Ed Tanous049a0512018-11-01 13:58:42 -0700296 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
297 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700298 return;
299 }
300
Ed Tanousf254ba72018-10-12 13:40:35 -0700301 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700303 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700304 {
305 if (connectionGroup.first == connection_name)
306 {
307 // Found the object manager path for this resource.
308 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700309 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600310 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700311 return;
312 }
313 }
314 }
315 },
316 "xyz.openbmc_project.ObjectMapper",
317 "/xyz/openbmc_project/object_mapper",
318 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
319 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700320}
Ed Tanous64530012018-02-06 17:08:16 -0800321
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600322// Uses GetObject to add the object info about the target /enumerate path to the
323// results of GetSubTree, as GetSubTree will not return info for the
324// target path, and then continues on enumerating the rest of the tree.
325void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
326{
327 using GetObjectType =
328 std::vector<std::pair<std::string, std::vector<std::string>>>;
329
330 crow::connections::systemBus->async_method_call(
331 [transaction](const boost::system::error_code ec,
332 const GetObjectType &objects) {
333 if (ec)
334 {
335 BMCWEB_LOG_ERROR << "GetObject for path "
336 << transaction->objectPath
337 << " failed with code " << ec;
338 return;
339 }
340
341 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
342 << " has " << objects.size() << " entries";
343 if (!objects.empty())
344 {
345 transaction->subtree->emplace_back(transaction->objectPath,
346 objects);
347 }
348
349 // Map indicating connection name, and the path where the object
350 // manager exists
351 boost::container::flat_map<std::string, std::string> connections;
352
353 for (const auto &object : *(transaction->subtree))
354 {
355 for (const auto &connection : object.second)
356 {
357 std::string &objectManagerPath =
358 connections[connection.first];
359 for (const auto &interface : connection.second)
360 {
361 BMCWEB_LOG_DEBUG << connection.first
362 << " has interface " << interface;
363 if (interface == "org.freedesktop.DBus.ObjectManager")
364 {
365 BMCWEB_LOG_DEBUG << "found object manager path "
366 << object.first;
367 objectManagerPath = object.first;
368 }
369 }
370 }
371 }
372 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
373
374 for (const auto &connection : connections)
375 {
376 // If we already know where the object manager is, we don't need
377 // to search for it, we can call directly in to
378 // getManagedObjects
379 if (!connection.second.empty())
380 {
381 getManagedObjectsForEnumerate(
382 transaction->objectPath, connection.second,
383 connection.first, transaction);
384 }
385 else
386 {
387 // otherwise we need to find the object manager path before
388 // we can continue
389 findObjectManagerPathForEnumerate(
390 transaction->objectPath, connection.first, transaction);
391 }
392 }
393 },
394 "xyz.openbmc_project.ObjectMapper",
395 "/xyz/openbmc_project/object_mapper",
396 "xyz.openbmc_project.ObjectMapper", "GetObject",
397 transaction->objectPath, std::array<const char *, 0>());
398}
Ed Tanous64530012018-02-06 17:08:16 -0800399
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700400// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700401struct InProgressActionData
402{
403 InProgressActionData(crow::Response &res) : res(res){};
404 ~InProgressActionData()
405 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600406 // Methods could have been called across different owners
407 // and interfaces, where some calls failed and some passed.
408 //
409 // The rules for this are:
410 // * if no method was called - error
411 // * if a method failed and none passed - error
412 // (converse: if at least one method passed - OK)
413 // * for the method output:
414 // * if output processing didn't fail, return the data
415
416 // Only deal with method returns if nothing failed earlier
417 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600419 if (!methodPassed)
420 {
421 if (methodFailed)
422 {
423 setErrorResponse(res,
424 boost::beast::http::status::bad_request,
425 "Method call failed", methodFailedMsg);
426 }
427 else
428 {
429 setErrorResponse(res, boost::beast::http::status::not_found,
430 methodNotFoundDesc, notFoundMsg);
431 }
432 }
433 else
434 {
435 if (outputFailed)
436 {
437 setErrorResponse(
438 res, boost::beast::http::status::internal_server_error,
439 "Method output failure", methodOutputFailedMsg);
440 }
441 else
442 {
443 res.jsonValue = {{"status", "ok"},
444 {"message", "200 OK"},
445 {"data", methodResponse}};
446 }
447 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600449
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700451 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700452
Matt Spinler6db06242018-12-11 11:21:22 -0600453 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600455 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
456 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 }
458 crow::Response &res;
459 std::string path;
460 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600461 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600462 bool methodPassed = false;
463 bool methodFailed = false;
464 bool outputFailed = false;
465 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700467};
468
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469std::vector<std::string> dbusArgSplit(const std::string &string)
470{
471 std::vector<std::string> ret;
472 if (string.empty())
473 {
474 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700475 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 ret.push_back("");
477 int containerDepth = 0;
478
479 for (std::string::const_iterator character = string.begin();
480 character != string.end(); character++)
481 {
482 ret.back() += *character;
483 switch (*character)
484 {
485 case ('a'):
486 break;
487 case ('('):
488 case ('{'):
489 containerDepth++;
490 break;
491 case ('}'):
492 case (')'):
493 containerDepth--;
494 if (containerDepth == 0)
495 {
496 if (character + 1 != string.end())
497 {
498 ret.push_back("");
499 }
500 }
501 break;
502 default:
503 if (containerDepth == 0)
504 {
505 if (character + 1 != string.end())
506 {
507 ret.push_back("");
508 }
509 }
510 break;
511 }
512 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600513
514 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700515}
516
Ed Tanousd76323e2018-08-07 14:35:40 -0700517int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 const nlohmann::json &input_json)
519{
520 int r = 0;
521 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
522 << " to type: " << arg_type;
523 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700524
Ed Tanous1abe55e2018-09-05 08:30:59 -0700525 // Assume a single object for now.
526 const nlohmann::json *j = &input_json;
527 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700528
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700529 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 {
531 // If we are decoding multiple objects, grab the pointer to the
532 // iterator, and increment it for the next loop
533 if (argTypes.size() > 1)
534 {
535 if (jIt == input_json.end())
536 {
537 return -2;
538 }
539 j = &*jIt;
540 jIt++;
541 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700542 const int64_t *intValue = j->get_ptr<const int64_t *>();
543 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
544 const std::string *stringValue = j->get_ptr<const std::string *>();
545 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 const bool *b = j->get_ptr<const bool *>();
547 int64_t v = 0;
548 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700549
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 // Do some basic type conversions that make sense. uint can be
551 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700554 v = static_cast<int64_t>(*uintValue);
555 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700557 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700559 d = static_cast<double>(*uintValue);
560 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700562 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700564 d = static_cast<double>(*intValue);
565 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700566 }
567
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700570 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 {
572 return -1;
573 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 r = sd_bus_message_append_basic(m, argCode[0],
575 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 if (r < 0)
577 {
578 return r;
579 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700580 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700583 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 {
585 return -1;
586 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 int32_t i = static_cast<int32_t>(*intValue);
588 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 if (r < 0)
590 {
591 return r;
592 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700593 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700594 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 {
596 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700597 int boolInt = false;
598 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 }
602 else if (b != nullptr)
603 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600604 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700608 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 }
610 else
611 {
612 return -1;
613 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700614 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 if (r < 0)
616 {
617 return r;
618 }
619 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 {
624 return -1;
625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 int16_t n = static_cast<int16_t>(*intValue);
627 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 if (r < 0)
629 {
630 return r;
631 }
632 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700633 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700635 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700636 {
637 return -1;
638 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 if (r < 0)
641 {
642 return r;
643 }
644 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700645 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700647 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 {
649 return -1;
650 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700651 uint8_t y = static_cast<uint8_t>(*uintValue);
652 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700656 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 {
658 return -1;
659 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 uint16_t q = static_cast<uint16_t>(*uintValue);
661 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700663 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 {
667 return -1;
668 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700669 uint32_t u = static_cast<uint32_t>(*uintValue);
670 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
676 return -1;
677 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700678 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700688 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 if (r < 0)
690 {
691 return r;
692 }
693
694 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
695 ++it)
696 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700697 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 if (r < 0)
699 {
700 return r;
701 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 }
703 sd_bus_message_close_container(m);
704 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700705 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700707 std::string containedType = argCode.substr(1);
708 BMCWEB_LOG_DEBUG << "variant type: " << argCode
709 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700711 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712 if (r < 0)
713 {
714 return r;
715 }
716
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700717 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 if (r < 0)
719 {
720 return r;
721 }
722
723 r = sd_bus_message_close_container(m);
724 if (r < 0)
725 {
726 return r;
727 }
728 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700729 else if (boost::starts_with(argCode, "(") &&
730 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700732 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700734 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700736 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 {
738 if (it == j->end())
739 {
740 return -1;
741 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700742 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 if (r < 0)
744 {
745 return r;
746 }
747 it++;
748 }
749 r = sd_bus_message_close_container(m);
750 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700751 else if (boost::starts_with(argCode, "{") &&
752 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700754 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700756 containedType.c_str());
757 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 if (codes.size() != 2)
759 {
760 return -1;
761 }
762 const std::string &key_type = codes[0];
763 const std::string &value_type = codes[1];
764 for (auto it : j->items())
765 {
766 r = convertJsonToDbus(m, key_type, it.key());
767 if (r < 0)
768 {
769 return r;
770 }
771
772 r = convertJsonToDbus(m, value_type, it.value());
773 if (r < 0)
774 {
775 return r;
776 }
777 }
778 r = sd_bus_message_close_container(m);
779 }
780 else
781 {
782 return -2;
783 }
784 if (r < 0)
785 {
786 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700787 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700788
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 if (argTypes.size() > 1)
790 {
791 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700792 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700793 }
Matt Spinler127ea542019-01-14 11:04:28 -0600794
795 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700796}
797
Matt Spinlerd22a7132019-01-14 12:14:30 -0600798template <typename T>
799int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
800 nlohmann::json &data)
801{
802 T value;
803
804 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
805 if (r < 0)
806 {
807 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
808 << " failed!";
809 return r;
810 }
811
812 data = value;
813 return 0;
814}
815
Matt Spinler16caaee2019-01-15 11:40:34 -0600816int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600817 sdbusplus::message::message &m, nlohmann::json &response);
818
819int readDictEntryFromMessage(const std::string &typeCode,
820 sdbusplus::message::message &m,
821 nlohmann::json &object)
822{
823 std::vector<std::string> types = dbusArgSplit(typeCode);
824 if (types.size() != 2)
825 {
826 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
827 << types.size();
828 return -1;
829 }
830
831 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
832 typeCode.c_str());
833 if (r < 0)
834 {
835 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
836 return r;
837 }
838
839 nlohmann::json key;
840 r = convertDBusToJSON(types[0], m, key);
841 if (r < 0)
842 {
843 return r;
844 }
845
846 const std::string *keyPtr = key.get_ptr<const std::string *>();
847 if (keyPtr == nullptr)
848 {
849 // json doesn't support non-string keys. If we hit this condition,
850 // convert the result to a string so we can proceed
851 key = key.dump();
852 keyPtr = key.get_ptr<const std::string *>();
853 // in theory this can't fail now, but lets be paranoid about it anyway
854 if (keyPtr == nullptr)
855 {
856 return -1;
857 }
858 }
859 nlohmann::json &value = object[*keyPtr];
860
861 r = convertDBusToJSON(types[1], m, value);
862 if (r < 0)
863 {
864 return r;
865 }
866
867 r = sd_bus_message_exit_container(m.get());
868 if (r < 0)
869 {
870 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
871 return r;
872 }
873
874 return 0;
875}
876
877int readArrayFromMessage(const std::string &typeCode,
878 sdbusplus::message::message &m, nlohmann::json &data)
879{
880 if (typeCode.size() < 2)
881 {
882 BMCWEB_LOG_ERROR << "Type code " << typeCode
883 << " too small for an array";
884 return -1;
885 }
886
887 std::string containedType = typeCode.substr(1);
888
889 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
890 containedType.c_str());
891 if (r < 0)
892 {
893 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
894 << r;
895 return r;
896 }
897
898 bool dict = boost::starts_with(containedType, "{") &&
899 boost::ends_with(containedType, "}");
900
901 if (dict)
902 {
903 // Remove the { }
904 containedType = containedType.substr(1, containedType.size() - 2);
905 data = nlohmann::json::object();
906 }
907 else
908 {
909 data = nlohmann::json::array();
910 }
911
912 while (true)
913 {
914 r = sd_bus_message_at_end(m.get(), false);
915 if (r < 0)
916 {
917 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
918 return r;
919 }
920
921 if (r > 0)
922 {
923 break;
924 }
925
926 // Dictionaries are only ever seen in an array
927 if (dict)
928 {
929 r = readDictEntryFromMessage(containedType, m, data);
930 if (r < 0)
931 {
932 return r;
933 }
934 }
935 else
936 {
937 data.push_back(nlohmann::json());
938
939 r = convertDBusToJSON(containedType, m, data.back());
940 if (r < 0)
941 {
942 return r;
943 }
944 }
945 }
946
947 r = sd_bus_message_exit_container(m.get());
948 if (r < 0)
949 {
950 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
951 return r;
952 }
953
954 return 0;
955}
956
Matt Spinler75c6c672019-01-14 13:01:46 -0600957int readStructFromMessage(const std::string &typeCode,
958 sdbusplus::message::message &m, nlohmann::json &data)
959{
960 if (typeCode.size() < 3)
961 {
962 BMCWEB_LOG_ERROR << "Type code " << typeCode
963 << " too small for a struct";
964 return -1;
965 }
966
967 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
968 std::vector<std::string> types = dbusArgSplit(containedTypes);
969
970 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
971 containedTypes.c_str());
972 if (r < 0)
973 {
974 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
975 << r;
976 return r;
977 }
978
979 for (const std::string &type : types)
980 {
981 data.push_back(nlohmann::json());
982 r = convertDBusToJSON(type, m, data.back());
983 if (r < 0)
984 {
985 return r;
986 }
987 }
988
989 r = sd_bus_message_exit_container(m.get());
990 if (r < 0)
991 {
992 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
993 return r;
994 }
995 return 0;
996}
997
Matt Spinler89c19702019-01-14 13:13:00 -0600998int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
999{
1000 const char *containerType;
1001 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1002 if (r < 0)
1003 {
1004 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1005 return r;
1006 }
1007
1008 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1009 containerType);
1010 if (r < 0)
1011 {
1012 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1013 << r;
1014 return r;
1015 }
1016
1017 r = convertDBusToJSON(containerType, m, data);
1018 if (r < 0)
1019 {
1020 return r;
1021 }
1022
1023 r = sd_bus_message_exit_container(m.get());
1024 if (r < 0)
1025 {
1026 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1027 return r;
1028 }
1029
1030 return 0;
1031}
1032
Matt Spinler6df8f992019-01-14 12:47:47 -06001033int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001034 sdbusplus::message::message &m, nlohmann::json &response)
1035{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001036 int r = 0;
1037 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1038
1039 nlohmann::json &thisElement = response;
1040 for (const std::string &typeCode : returnTypes)
1041 {
1042 if (returnType.size() > 1)
1043 {
1044 response.push_back(nlohmann::json{});
1045 thisElement = response.back();
1046 }
1047
1048 if (typeCode == "s")
1049 {
1050 r = readMessageItem<char *>(typeCode, m, thisElement);
1051 if (r < 0)
1052 {
1053 return r;
1054 }
1055 }
1056 else if (typeCode == "g")
1057 {
1058 r = readMessageItem<char *>(typeCode, m, thisElement);
1059 if (r < 0)
1060 {
1061 return r;
1062 }
1063 }
1064 else if (typeCode == "o")
1065 {
1066 r = readMessageItem<char *>(typeCode, m, thisElement);
1067 if (r < 0)
1068 {
1069 return r;
1070 }
1071 }
1072 else if (typeCode == "b")
1073 {
1074 r = readMessageItem<int>(typeCode, m, thisElement);
1075 if (r < 0)
1076 {
1077 return r;
1078 }
1079
1080 thisElement = static_cast<bool>(thisElement.get<int>());
1081 }
1082 else if (typeCode == "u")
1083 {
1084 r = readMessageItem<uint32_t>(typeCode, m, thisElement);
1085 if (r < 0)
1086 {
1087 return r;
1088 }
1089 }
1090 else if (typeCode == "i")
1091 {
1092 r = readMessageItem<int32_t>(typeCode, m, thisElement);
1093 if (r < 0)
1094 {
1095 return r;
1096 }
1097 }
1098 else if (typeCode == "x")
1099 {
1100 r = readMessageItem<int64_t>(typeCode, m, thisElement);
1101 if (r < 0)
1102 {
1103 return r;
1104 }
1105 }
1106 else if (typeCode == "t")
1107 {
1108 r = readMessageItem<uint64_t>(typeCode, m, thisElement);
1109 if (r < 0)
1110 {
1111 return r;
1112 }
1113 }
1114 else if (typeCode == "n")
1115 {
1116 r = readMessageItem<int16_t>(typeCode, m, thisElement);
1117 if (r < 0)
1118 {
1119 return r;
1120 }
1121 }
1122 else if (typeCode == "q")
1123 {
1124 r = readMessageItem<uint16_t>(typeCode, m, thisElement);
1125 if (r < 0)
1126 {
1127 return r;
1128 }
1129 }
1130 else if (typeCode == "y")
1131 {
1132 r = readMessageItem<uint8_t>(typeCode, m, thisElement);
1133 if (r < 0)
1134 {
1135 return r;
1136 }
1137 }
1138 else if (typeCode == "d")
1139 {
1140 r = readMessageItem<double>(typeCode, m, thisElement);
1141 if (r < 0)
1142 {
1143 return r;
1144 }
1145 }
1146 else if (typeCode == "h")
1147 {
1148 r = readMessageItem<int>(typeCode, m, thisElement);
1149 if (r < 0)
1150 {
1151 return r;
1152 }
1153 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001154 else if (boost::starts_with(typeCode, "a"))
1155 {
1156 r = readArrayFromMessage(typeCode, m, thisElement);
1157 if (r < 0)
1158 {
1159 return r;
1160 }
1161 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001162 else if (boost::starts_with(typeCode, "(") &&
1163 boost::ends_with(typeCode, ")"))
1164 {
1165 r = readStructFromMessage(typeCode, m, thisElement);
1166 if (r < 0)
1167 {
1168 return r;
1169 }
1170 }
Matt Spinler89c19702019-01-14 13:13:00 -06001171 else if (boost::starts_with(typeCode, "v"))
1172 {
1173 r = readVariantFromMessage(m, thisElement);
1174 if (r < 0)
1175 {
1176 return r;
1177 }
1178 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001179 else
1180 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001181 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1182 return -2;
1183 }
1184 }
1185
Matt Spinler16caaee2019-01-15 11:40:34 -06001186 return 0;
1187}
1188
1189void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1190 sdbusplus::message::message &m,
1191 const std::string &returnType)
1192{
1193}
1194
Ed Tanousd76323e2018-08-07 14:35:40 -07001195void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001196 const std::string &connectionName)
1197{
1198 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1199 << connectionName;
1200 crow::connections::systemBus->async_method_call(
1201 [transaction, connectionName{std::string(connectionName)}](
1202 const boost::system::error_code ec,
1203 const std::string &introspect_xml) {
1204 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1205 if (ec)
1206 {
1207 BMCWEB_LOG_ERROR
1208 << "Introspect call failed with error: " << ec.message()
1209 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001210 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001211 }
Matt Spinler318bd892019-01-15 09:59:20 -06001212 tinyxml2::XMLDocument doc;
1213
1214 doc.Parse(introspect_xml.data(), introspect_xml.size());
1215 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1216 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001217 {
Matt Spinler318bd892019-01-15 09:59:20 -06001218 BMCWEB_LOG_ERROR << "XML document failed to parse "
1219 << connectionName << "\n";
1220 return;
1221 }
1222 tinyxml2::XMLElement *interfaceNode =
1223 pRoot->FirstChildElement("interface");
1224 while (interfaceNode != nullptr)
1225 {
1226 const char *thisInterfaceName =
1227 interfaceNode->Attribute("name");
1228 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001229 {
Matt Spinler318bd892019-01-15 09:59:20 -06001230 if (!transaction->interfaceName.empty() &&
1231 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001232 {
Matt Spinler318bd892019-01-15 09:59:20 -06001233 interfaceNode =
1234 interfaceNode->NextSiblingElement("interface");
1235 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001236 }
Matt Spinler318bd892019-01-15 09:59:20 -06001237
1238 tinyxml2::XMLElement *methodNode =
1239 interfaceNode->FirstChildElement("method");
1240 while (methodNode != nullptr)
1241 {
1242 const char *thisMethodName =
1243 methodNode->Attribute("name");
1244 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1245 if (thisMethodName != nullptr &&
1246 thisMethodName == transaction->methodName)
1247 {
1248 BMCWEB_LOG_DEBUG
1249 << "Found method named " << thisMethodName
1250 << " on interface " << thisInterfaceName;
1251 sdbusplus::message::message m =
1252 crow::connections::systemBus->new_method_call(
1253 connectionName.c_str(),
1254 transaction->path.c_str(),
1255 thisInterfaceName,
1256 transaction->methodName.c_str());
1257
1258 tinyxml2::XMLElement *argumentNode =
1259 methodNode->FirstChildElement("arg");
1260
Matt Spinler16caaee2019-01-15 11:40:34 -06001261 std::string returnType;
1262
1263 // Find the output type
1264 while (argumentNode != nullptr)
1265 {
1266 const char *argDirection =
1267 argumentNode->Attribute("direction");
1268 const char *argType =
1269 argumentNode->Attribute("type");
1270 if (argDirection != nullptr &&
1271 argType != nullptr &&
1272 std::string(argDirection) == "out")
1273 {
1274 returnType = argType;
1275 break;
1276 }
1277 argumentNode =
1278 argumentNode->NextSiblingElement("arg");
1279 }
1280
Matt Spinler318bd892019-01-15 09:59:20 -06001281 nlohmann::json::const_iterator argIt =
1282 transaction->arguments.begin();
1283
Matt Spinler16caaee2019-01-15 11:40:34 -06001284 argumentNode = methodNode->FirstChildElement("arg");
1285
Matt Spinler318bd892019-01-15 09:59:20 -06001286 while (argumentNode != nullptr)
1287 {
1288 const char *argDirection =
1289 argumentNode->Attribute("direction");
1290 const char *argType =
1291 argumentNode->Attribute("type");
1292 if (argDirection != nullptr &&
1293 argType != nullptr &&
1294 std::string(argDirection) == "in")
1295 {
1296 if (argIt == transaction->arguments.end())
1297 {
1298 transaction->setErrorStatus(
1299 "Invalid method args");
1300 return;
1301 }
1302 if (convertJsonToDbus(m.get(),
1303 std::string(argType),
1304 *argIt) < 0)
1305 {
1306 transaction->setErrorStatus(
1307 "Invalid method arg type");
1308 return;
1309 }
1310
1311 argIt++;
1312 }
1313 argumentNode =
1314 argumentNode->NextSiblingElement("arg");
1315 }
1316
1317 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001318 m, [transaction, returnType](
1319 boost::system::error_code ec,
1320 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001321 if (ec)
1322 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001323 transaction->methodFailed = true;
Matt Spinler318bd892019-01-15 09:59:20 -06001324 return;
1325 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001326 else
1327 {
1328 transaction->methodPassed = true;
1329 }
1330
1331 handleMethodResponse(transaction, m,
1332 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001333 });
1334 break;
1335 }
1336 methodNode = methodNode->NextSiblingElement("method");
1337 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001338 }
Matt Spinler318bd892019-01-15 09:59:20 -06001339 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001340 }
1341 },
1342 connectionName, transaction->path,
1343 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001344}
1345
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001346void handleAction(const crow::Request &req, crow::Response &res,
1347 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001349 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1350 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001351 nlohmann::json requestDbusData =
1352 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001353
Ed Tanous1abe55e2018-09-05 08:30:59 -07001354 if (requestDbusData.is_discarded())
1355 {
Matt Spinler6db06242018-12-11 11:21:22 -06001356 setErrorResponse(res, boost::beast::http::status::bad_request,
1357 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001358 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001359 return;
1360 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001361 nlohmann::json::iterator data = requestDbusData.find("data");
1362 if (data == requestDbusData.end())
1363 {
Matt Spinler6db06242018-12-11 11:21:22 -06001364 setErrorResponse(res, boost::beast::http::status::bad_request,
1365 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001366 res.end();
1367 return;
1368 }
1369
1370 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001371 {
Matt Spinler6db06242018-12-11 11:21:22 -06001372 setErrorResponse(res, boost::beast::http::status::bad_request,
1373 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001374 res.end();
1375 return;
1376 }
1377 auto transaction = std::make_shared<InProgressActionData>(res);
1378
1379 transaction->path = objectPath;
1380 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001381 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382 crow::connections::systemBus->async_method_call(
1383 [transaction](
1384 const boost::system::error_code ec,
1385 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001386 &interfaceNames) {
1387 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001388 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001389 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001390 setErrorResponse(transaction->res,
1391 boost::beast::http::status::not_found,
1392 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001393 return;
1394 }
1395
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001396 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1397 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001398
1399 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001400 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001401 {
1402 findActionOnInterface(transaction, object.first);
1403 }
1404 },
1405 "xyz.openbmc_project.ObjectMapper",
1406 "/xyz/openbmc_project/object_mapper",
1407 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1408 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001409}
1410
Matt Spinlerde818812018-12-11 16:39:20 -06001411void handleDelete(const crow::Request &req, crow::Response &res,
1412 const std::string &objectPath)
1413{
1414 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1415
1416 crow::connections::systemBus->async_method_call(
1417 [&res, objectPath](
1418 const boost::system::error_code ec,
1419 const std::vector<std::pair<std::string, std::vector<std::string>>>
1420 &interfaceNames) {
1421 if (ec || interfaceNames.size() <= 0)
1422 {
1423 BMCWEB_LOG_ERROR << "Can't find object";
1424 setErrorResponse(res, boost::beast::http::status::not_found,
1425 notFoundDesc, notFoundMsg);
1426 res.end();
1427 return;
1428 }
1429
1430 auto transaction = std::make_shared<InProgressActionData>(res);
1431 transaction->path = objectPath;
1432 transaction->methodName = "Delete";
1433 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1434
1435 for (const std::pair<std::string, std::vector<std::string>>
1436 &object : interfaceNames)
1437 {
1438 findActionOnInterface(transaction, object.first);
1439 }
1440 },
1441 "xyz.openbmc_project.ObjectMapper",
1442 "/xyz/openbmc_project/object_mapper",
1443 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1444 std::array<const char *, 0>());
1445}
1446
Ed Tanousf839dfe2018-11-12 11:11:15 -08001447void handleList(crow::Response &res, const std::string &objectPath,
1448 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001449{
1450 crow::connections::systemBus->async_method_call(
1451 [&res](const boost::system::error_code ec,
1452 std::vector<std::string> &objectPaths) {
1453 if (ec)
1454 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001455 setErrorResponse(res, boost::beast::http::status::not_found,
1456 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001457 }
1458 else
1459 {
1460 res.jsonValue = {{"status", "ok"},
1461 {"message", "200 OK"},
1462 {"data", std::move(objectPaths)}};
1463 }
1464 res.end();
1465 },
1466 "xyz.openbmc_project.ObjectMapper",
1467 "/xyz/openbmc_project/object_mapper",
1468 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001469 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001470}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001471
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001472void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001473{
Ed Tanous049a0512018-11-01 13:58:42 -07001474 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1475 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1476
1477 asyncResp->res.jsonValue = {{"message", "200 OK"},
1478 {"status", "ok"},
1479 {"data", nlohmann::json::object()}};
1480
Ed Tanous1abe55e2018-09-05 08:30:59 -07001481 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001482 [objectPath, asyncResp](const boost::system::error_code ec,
1483 GetSubTreeType &object_names) {
1484 auto transaction = std::make_shared<InProgressEnumerateData>(
1485 objectPath, asyncResp);
1486
1487 transaction->subtree =
1488 std::make_shared<GetSubTreeType>(std::move(object_names));
1489
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 if (ec)
1491 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001492 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1493 << transaction->objectPath;
1494 setErrorResponse(transaction->asyncResp->res,
1495 boost::beast::http::status::not_found,
1496 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001497 return;
1498 }
Ed Tanous64530012018-02-06 17:08:16 -08001499
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001500 // Add the data for the path passed in to the results
1501 // as if GetSubTree returned it, and continue on enumerating
1502 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001503 },
1504 "xyz.openbmc_project.ObjectMapper",
1505 "/xyz/openbmc_project/object_mapper",
1506 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001507 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001508}
Ed Tanous911ac312017-08-15 09:37:42 -07001509
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001510void handleGet(crow::Response &res, std::string &objectPath,
1511 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001512{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001513 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1514 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001515 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001516
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 std::shared_ptr<std::string> path =
1518 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001519
Ed Tanous1abe55e2018-09-05 08:30:59 -07001520 using GetObjectType =
1521 std::vector<std::pair<std::string, std::vector<std::string>>>;
1522 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001523 [&res, path, propertyName](const boost::system::error_code ec,
1524 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001525 if (ec || object_names.size() <= 0)
1526 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001527 setErrorResponse(res, boost::beast::http::status::not_found,
1528 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 res.end();
1530 return;
1531 }
1532 std::shared_ptr<nlohmann::json> response =
1533 std::make_shared<nlohmann::json>(nlohmann::json::object());
1534 // The mapper should never give us an empty interface names list,
1535 // but check anyway
1536 for (const std::pair<std::string, std::vector<std::string>>
1537 connection : object_names)
1538 {
1539 const std::vector<std::string> &interfaceNames =
1540 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001541
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542 if (interfaceNames.size() <= 0)
1543 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001544 setErrorResponse(res, boost::beast::http::status::not_found,
1545 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001546 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001547 return;
1548 }
1549
1550 for (const std::string &interface : interfaceNames)
1551 {
1552 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001553 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001554 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001555 const std::vector<std::pair<
1556 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001557 &properties) {
1558 if (ec)
1559 {
1560 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1561 << ec;
1562 }
1563 else
1564 {
James Feist5b4aa862018-08-16 14:07:01 -07001565 for (const std::pair<
1566 std::string,
1567 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001568 &property : properties)
1569 {
1570 // if property name is empty, or matches our
1571 // search query, add it to the response json
1572
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001573 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001574 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001575 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001576 [&response, &property](auto &&val) {
1577 (*response)[property.first] =
1578 val;
1579 },
1580 property.second);
1581 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001582 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001583 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001584 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585 [&response](auto &&val) {
1586 (*response) = val;
1587 },
1588 property.second);
1589 }
1590 }
1591 }
1592 if (response.use_count() == 1)
1593 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001594 if (!propertyName->empty() && response->empty())
1595 {
1596 setErrorResponse(
1597 res,
1598 boost::beast::http::status::not_found,
1599 propNotFoundDesc, notFoundMsg);
1600 }
1601 else
1602 {
1603 res.jsonValue = {{"status", "ok"},
1604 {"message", "200 OK"},
1605 {"data", *response}};
1606 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607 res.end();
1608 }
1609 },
1610 connection.first, *path,
1611 "org.freedesktop.DBus.Properties", "GetAll", interface);
1612 }
1613 }
1614 },
1615 "xyz.openbmc_project.ObjectMapper",
1616 "/xyz/openbmc_project/object_mapper",
1617 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1618 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001619}
1620
Ed Tanous1abe55e2018-09-05 08:30:59 -07001621struct AsyncPutRequest
1622{
1623 AsyncPutRequest(crow::Response &res) : res(res)
1624 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001625 }
1626 ~AsyncPutRequest()
1627 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001628 if (res.jsonValue.empty())
1629 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001630 setErrorResponse(res, boost::beast::http::status::forbidden,
1631 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001632 }
1633
1634 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001635 }
1636
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001637 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001638 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001639 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1640 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001641 }
1642
Ed Tanous1abe55e2018-09-05 08:30:59 -07001643 crow::Response &res;
1644 std::string objectPath;
1645 std::string propertyName;
1646 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001647};
1648
Ed Tanousd76323e2018-08-07 14:35:40 -07001649void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001650 const std::string &objectPath, const std::string &destProperty)
1651{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001652 if (destProperty.empty())
1653 {
1654 setErrorResponse(res, boost::beast::http::status::forbidden,
1655 forbiddenResDesc, forbiddenMsg);
1656 res.end();
1657 return;
1658 }
1659
Ed Tanous1abe55e2018-09-05 08:30:59 -07001660 nlohmann::json requestDbusData =
1661 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001662
Ed Tanous1abe55e2018-09-05 08:30:59 -07001663 if (requestDbusData.is_discarded())
1664 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001665 setErrorResponse(res, boost::beast::http::status::bad_request,
1666 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001667 res.end();
1668 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001669 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001670
Ed Tanous1abe55e2018-09-05 08:30:59 -07001671 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1672 if (propertyIt == requestDbusData.end())
1673 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001674 setErrorResponse(res, boost::beast::http::status::bad_request,
1675 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001676 res.end();
1677 return;
1678 }
1679 const nlohmann::json &propertySetValue = *propertyIt;
1680 auto transaction = std::make_shared<AsyncPutRequest>(res);
1681 transaction->objectPath = objectPath;
1682 transaction->propertyName = destProperty;
1683 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001684
Ed Tanous1abe55e2018-09-05 08:30:59 -07001685 using GetObjectType =
1686 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001687
Ed Tanous1abe55e2018-09-05 08:30:59 -07001688 crow::connections::systemBus->async_method_call(
1689 [transaction](const boost::system::error_code ec,
1690 const GetObjectType &object_names) {
1691 if (!ec && object_names.size() <= 0)
1692 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001693 setErrorResponse(transaction->res,
1694 boost::beast::http::status::not_found,
1695 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001696 return;
1697 }
Ed Tanous911ac312017-08-15 09:37:42 -07001698
Ed Tanous1abe55e2018-09-05 08:30:59 -07001699 for (const std::pair<std::string, std::vector<std::string>>
1700 connection : object_names)
1701 {
1702 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001703
Ed Tanous1abe55e2018-09-05 08:30:59 -07001704 crow::connections::systemBus->async_method_call(
1705 [connectionName{std::string(connectionName)},
1706 transaction](const boost::system::error_code ec,
1707 const std::string &introspectXml) {
1708 if (ec)
1709 {
1710 BMCWEB_LOG_ERROR
1711 << "Introspect call failed with error: "
1712 << ec.message()
1713 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001714 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001715 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001716 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001717 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001718
Ed Tanous1abe55e2018-09-05 08:30:59 -07001719 doc.Parse(introspectXml.c_str());
1720 tinyxml2::XMLNode *pRoot =
1721 doc.FirstChildElement("node");
1722 if (pRoot == nullptr)
1723 {
1724 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1725 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001726 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001727 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001728 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001729 tinyxml2::XMLElement *ifaceNode =
1730 pRoot->FirstChildElement("interface");
1731 while (ifaceNode != nullptr)
1732 {
1733 const char *interfaceName =
1734 ifaceNode->Attribute("name");
1735 BMCWEB_LOG_DEBUG << "found interface "
1736 << interfaceName;
1737 tinyxml2::XMLElement *propNode =
1738 ifaceNode->FirstChildElement("property");
1739 while (propNode != nullptr)
1740 {
1741 const char *propertyName =
1742 propNode->Attribute("name");
1743 BMCWEB_LOG_DEBUG << "Found property "
1744 << propertyName;
1745 if (propertyName == transaction->propertyName)
1746 {
1747 const char *argType =
1748 propNode->Attribute("type");
1749 if (argType != nullptr)
1750 {
1751 sdbusplus::message::message m =
1752 crow::connections::systemBus
1753 ->new_method_call(
1754 connectionName.c_str(),
1755 transaction->objectPath
1756 .c_str(),
1757 "org.freedesktop.DBus."
1758 "Properties",
1759 "Set");
1760 m.append(interfaceName,
1761 transaction->propertyName);
1762 int r = sd_bus_message_open_container(
1763 m.get(), SD_BUS_TYPE_VARIANT,
1764 argType);
1765 if (r < 0)
1766 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001767 transaction->setErrorStatus(
1768 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001769 return;
1770 }
1771 r = convertJsonToDbus(
1772 m.get(), argType,
1773 transaction->propertyValue);
1774 if (r < 0)
1775 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001776 transaction->setErrorStatus(
1777 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001778 return;
1779 }
1780 r = sd_bus_message_close_container(
1781 m.get());
1782 if (r < 0)
1783 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001784 transaction->setErrorStatus(
1785 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001786 return;
1787 }
Ed Tanous911ac312017-08-15 09:37:42 -07001788
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 crow::connections::systemBus
1790 ->async_send(
1791 m,
1792 [transaction](
1793 boost::system::error_code
1794 ec,
1795 sdbusplus::message::message
1796 &m) {
1797 BMCWEB_LOG_DEBUG << "sent";
1798 if (ec)
1799 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001800 setErrorResponse(
1801 transaction->res,
1802 boost::beast::http::
1803 status::
1804 forbidden,
1805 forbiddenPropDesc,
1806 ec.message());
1807 }
1808 else
1809 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001810 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001811 .jsonValue = {
1812 {"status", "ok"},
1813 {"message",
1814 "200 OK"},
1815 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001816 }
1817 });
1818 }
1819 }
1820 propNode =
1821 propNode->NextSiblingElement("property");
1822 }
1823 ifaceNode =
1824 ifaceNode->NextSiblingElement("interface");
1825 }
1826 },
1827 connectionName, transaction->objectPath,
1828 "org.freedesktop.DBus.Introspectable", "Introspect");
1829 }
1830 },
1831 "xyz.openbmc_project.ObjectMapper",
1832 "/xyz/openbmc_project/object_mapper",
1833 "xyz.openbmc_project.ObjectMapper", "GetObject",
1834 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001835}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001836
Ed Tanous049a0512018-11-01 13:58:42 -07001837inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1838 std::string &objectPath)
1839{
Ed Tanous049a0512018-11-01 13:58:42 -07001840
1841 // If accessing a single attribute, fill in and update objectPath,
1842 // otherwise leave destProperty blank
1843 std::string destProperty = "";
1844 const char *attrSeperator = "/attr/";
1845 size_t attrPosition = objectPath.find(attrSeperator);
1846 if (attrPosition != objectPath.npos)
1847 {
1848 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1849 objectPath.length());
1850 objectPath = objectPath.substr(0, attrPosition);
1851 }
1852
1853 if (req.method() == "POST"_method)
1854 {
1855 constexpr const char *actionSeperator = "/action/";
1856 size_t actionPosition = objectPath.find(actionSeperator);
1857 if (actionPosition != objectPath.npos)
1858 {
1859 std::string postProperty =
1860 objectPath.substr((actionPosition + strlen(actionSeperator)),
1861 objectPath.length());
1862 objectPath = objectPath.substr(0, actionPosition);
1863 handleAction(req, res, objectPath, postProperty);
1864 return;
1865 }
1866 }
1867 else if (req.method() == "GET"_method)
1868 {
1869 if (boost::ends_with(objectPath, "/enumerate"))
1870 {
1871 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1872 objectPath.end());
1873 handleEnumerate(res, objectPath);
1874 }
1875 else if (boost::ends_with(objectPath, "/list"))
1876 {
1877 objectPath.erase(objectPath.end() - sizeof("list"),
1878 objectPath.end());
1879 handleList(res, objectPath);
1880 }
1881 else
1882 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001883 // Trim any trailing "/" at the end
1884 if (boost::ends_with(objectPath, "/"))
1885 {
1886 objectPath.pop_back();
1887 handleList(res, objectPath, 1);
1888 }
1889 else
1890 {
1891 handleGet(res, objectPath, destProperty);
1892 }
Ed Tanous049a0512018-11-01 13:58:42 -07001893 }
1894 return;
1895 }
1896 else if (req.method() == "PUT"_method)
1897 {
1898 handlePut(req, res, objectPath, destProperty);
1899 return;
1900 }
Matt Spinlerde818812018-12-11 16:39:20 -06001901 else if (req.method() == "DELETE"_method)
1902 {
1903 handleDelete(req, res, objectPath);
1904 return;
1905 }
Ed Tanous049a0512018-11-01 13:58:42 -07001906
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001907 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1908 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001909 res.end();
1910}
1911
Ed Tanous1abe55e2018-09-05 08:30:59 -07001912template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1913{
1914 BMCWEB_ROUTE(app, "/bus/")
1915 .methods("GET"_method)(
1916 [](const crow::Request &req, crow::Response &res) {
1917 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1918 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001919 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001920 });
1921
1922 BMCWEB_ROUTE(app, "/bus/system/")
1923 .methods("GET"_method)(
1924 [](const crow::Request &req, crow::Response &res) {
1925 auto myCallback = [&res](const boost::system::error_code ec,
1926 std::vector<std::string> &names) {
1927 if (ec)
1928 {
1929 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1930 res.result(
1931 boost::beast::http::status::internal_server_error);
1932 }
1933 else
1934 {
1935 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001936 res.jsonValue = {{"status", "ok"}};
1937 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001938 for (auto &name : names)
1939 {
1940 objectsSub.push_back({{"name", name}});
1941 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001942 }
1943 res.end();
1944 };
1945 crow::connections::systemBus->async_method_call(
1946 std::move(myCallback), "org.freedesktop.DBus", "/",
1947 "org.freedesktop.DBus", "ListNames");
1948 });
1949
1950 BMCWEB_ROUTE(app, "/list/")
1951 .methods("GET"_method)(
1952 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001953 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001954 });
1955
1956 BMCWEB_ROUTE(app, "/xyz/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06001957 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07001958 [](const crow::Request &req, crow::Response &res,
1959 const std::string &path) {
1960 std::string objectPath = "/xyz/" + path;
1961 handleDBusUrl(req, res, objectPath);
1962 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001963
Ed Tanous049a0512018-11-01 13:58:42 -07001964 BMCWEB_ROUTE(app, "/org/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06001965 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07001966 [](const crow::Request &req, crow::Response &res,
1967 const std::string &path) {
1968 std::string objectPath = "/org/" + path;
1969 handleDBusUrl(req, res, objectPath);
1970 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001971
Ed Tanous1abe55e2018-09-05 08:30:59 -07001972 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1973 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1974 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08001975 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001976 if (!std::regex_match(dumpId, validFilename))
1977 {
Ed Tanousad18f072018-11-14 14:07:48 -08001978 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001979 res.end();
1980 return;
1981 }
James Feistf6150402019-01-08 10:36:20 -08001982 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001983 "/var/lib/phosphor-debug-collector/dumps");
1984
Ed Tanousad18f072018-11-14 14:07:48 -08001985 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001986
James Feistf6150402019-01-08 10:36:20 -08001987 if (!std::filesystem::exists(loc) ||
1988 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001989 {
Ed Tanousad18f072018-11-14 14:07:48 -08001990 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001991 res.result(boost::beast::http::status::not_found);
1992 res.end();
1993 return;
1994 }
James Feistf6150402019-01-08 10:36:20 -08001995 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08001996
Ed Tanous1abe55e2018-09-05 08:30:59 -07001997 for (auto &file : files)
1998 {
1999 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002000 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002001 {
2002 continue;
2003 }
2004 res.addHeader("Content-Type", "application/octet-stream");
2005 res.body() = {std::istreambuf_iterator<char>(readFile),
2006 std::istreambuf_iterator<char>()};
2007 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002008 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002009 }
2010 res.result(boost::beast::http::status::not_found);
2011 res.end();
2012 return;
2013 });
2014
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002015 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07002016 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002017 const std::string &Connection) {
2018 introspectObjects(Connection, "/",
2019 std::make_shared<bmcweb::AsyncResp>(res));
2020 });
2021
2022 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2023 .methods("GET"_method,
2024 "POST"_method)([](const crow::Request &req,
2025 crow::Response &res,
2026 const std::string &processName,
2027 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002028 std::vector<std::string> strs;
2029 boost::split(strs, requestedPath, boost::is_any_of("/"));
2030 std::string objectPath;
2031 std::string interfaceName;
2032 std::string methodName;
2033 auto it = strs.begin();
2034 if (it == strs.end())
2035 {
2036 objectPath = "/";
2037 }
2038 while (it != strs.end())
2039 {
2040 // Check if segment contains ".". If it does, it must be an
2041 // interface
2042 if (it->find(".") != std::string::npos)
2043 {
2044 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002045 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07002046 // as part of our <path> specifier above, which causes the
2047 // normal trailing backslash redirector to fail.
2048 }
2049 else if (!it->empty())
2050 {
2051 objectPath += "/" + *it;
2052 }
2053 it++;
2054 }
2055 if (it != strs.end())
2056 {
2057 interfaceName = *it;
2058 it++;
2059
2060 // after interface, we might have a method name
2061 if (it != strs.end())
2062 {
2063 methodName = *it;
2064 it++;
2065 }
2066 }
2067 if (it != strs.end())
2068 {
2069 // if there is more levels past the method name, something went
2070 // wrong, return not found
2071 res.result(boost::beast::http::status::not_found);
2072 res.end();
2073 return;
2074 }
2075 if (interfaceName.empty())
2076 {
2077 crow::connections::systemBus->async_method_call(
2078 [&, processName,
2079 objectPath](const boost::system::error_code ec,
2080 const std::string &introspect_xml) {
2081 if (ec)
2082 {
2083 BMCWEB_LOG_ERROR
2084 << "Introspect call failed with error: "
2085 << ec.message()
2086 << " on process: " << processName
2087 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002088 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002089 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002090 tinyxml2::XMLDocument doc;
2091
2092 doc.Parse(introspect_xml.c_str());
2093 tinyxml2::XMLNode *pRoot =
2094 doc.FirstChildElement("node");
2095 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002096 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002097 BMCWEB_LOG_ERROR << "XML document failed to parse "
2098 << processName << " " << objectPath
2099 << "\n";
2100 res.jsonValue = {{"status", "XML parse error"}};
2101 res.result(boost::beast::http::status::
2102 internal_server_error);
2103 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002104 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002105
2106 BMCWEB_LOG_DEBUG << introspect_xml;
2107 res.jsonValue = {{"status", "ok"},
2108 {"bus_name", processName},
2109 {"object_path", objectPath}};
2110 nlohmann::json &interfacesArray =
2111 res.jsonValue["interfaces"];
2112 interfacesArray = nlohmann::json::array();
2113 tinyxml2::XMLElement *interface =
2114 pRoot->FirstChildElement("interface");
2115
2116 while (interface != nullptr)
2117 {
2118 const char *ifaceName =
2119 interface->Attribute("name");
2120 if (ifaceName != nullptr)
2121 {
2122 interfacesArray.push_back(
2123 {{"name", ifaceName}});
2124 }
2125
2126 interface =
2127 interface->NextSiblingElement("interface");
2128 }
2129
Ed Tanous1abe55e2018-09-05 08:30:59 -07002130 res.end();
2131 },
2132 processName, objectPath,
2133 "org.freedesktop.DBus.Introspectable", "Introspect");
2134 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002135 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002136 {
2137 crow::connections::systemBus->async_method_call(
2138 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002139 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07002140 const boost::system::error_code ec,
2141 const std::string &introspect_xml) {
2142 if (ec)
2143 {
2144 BMCWEB_LOG_ERROR
2145 << "Introspect call failed with error: "
2146 << ec.message()
2147 << " on process: " << processName
2148 << " path: " << objectPath << "\n";
2149 }
2150 else
2151 {
2152 tinyxml2::XMLDocument doc;
2153
2154 doc.Parse(introspect_xml.c_str());
2155 tinyxml2::XMLNode *pRoot =
2156 doc.FirstChildElement("node");
2157 if (pRoot == nullptr)
2158 {
2159 BMCWEB_LOG_ERROR
2160 << "XML document failed to parse "
2161 << processName << " " << objectPath << "\n";
2162 res.result(boost::beast::http::status::
2163 internal_server_error);
2164 }
2165 else
2166 {
2167 tinyxml2::XMLElement *node =
2168 pRoot->FirstChildElement("node");
2169
2170 // if we know we're the only call, build the
2171 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07002172 tinyxml2::XMLElement *interface =
2173 pRoot->FirstChildElement("interface");
2174
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002175 res.jsonValue = {
2176 {"status", "ok"},
2177 {"bus_name", processName},
2178 {"interface", interfaceName},
2179 {"object_path", objectPath},
2180 {"properties", nlohmann::json::object()}};
2181
2182 nlohmann::json &methodsArray =
2183 res.jsonValue["methods"];
2184 methodsArray = nlohmann::json::array();
2185
2186 nlohmann::json &signalsArray =
2187 res.jsonValue["signals"];
2188 signalsArray = nlohmann::json::array();
2189
Ed Tanous1abe55e2018-09-05 08:30:59 -07002190 while (interface != nullptr)
2191 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002192 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002193 interface->Attribute("name");
2194
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002195 if (ifaceName != nullptr &&
2196 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002197 {
2198 tinyxml2::XMLElement *methods =
2199 interface->FirstChildElement(
2200 "method");
2201 while (methods != nullptr)
2202 {
2203 nlohmann::json argsArray =
2204 nlohmann::json::array();
2205 tinyxml2::XMLElement *arg =
2206 methods->FirstChildElement(
2207 "arg");
2208 while (arg != nullptr)
2209 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002210 nlohmann::json thisArg;
2211 for (const char *fieldName :
2212 std::array<const char *,
2213 3>{"name",
2214 "direction",
2215 "type"})
2216 {
2217 const char *fieldValue =
2218 arg->Attribute(
2219 fieldName);
2220 if (fieldValue != nullptr)
2221 {
2222 thisArg[fieldName] =
2223 fieldValue;
2224 }
2225 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002226 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002227 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07002228 arg = arg->NextSiblingElement(
2229 "arg");
2230 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002231
2232 const char *name =
2233 methods->Attribute("name");
2234 if (name != nullptr)
2235 {
2236 methodsArray.push_back(
2237 {{"name", name},
2238 {"uri", "/bus/system/" +
2239 processName +
2240 objectPath +
2241 "/" +
2242 interfaceName +
2243 "/" + name},
2244 {"args", argsArray}});
2245 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002246 methods =
2247 methods->NextSiblingElement(
2248 "method");
2249 }
2250 tinyxml2::XMLElement *signals =
2251 interface->FirstChildElement(
2252 "signal");
2253 while (signals != nullptr)
2254 {
2255 nlohmann::json argsArray =
2256 nlohmann::json::array();
2257
2258 tinyxml2::XMLElement *arg =
2259 signals->FirstChildElement(
2260 "arg");
2261 while (arg != nullptr)
2262 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002263 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002264 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002265 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002266 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002267 if (name != nullptr &&
2268 type != nullptr)
2269 {
2270 argsArray.push_back({
2271 {"name", name},
2272 {"type", type},
2273 });
2274 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002275 arg = arg->NextSiblingElement(
2276 "arg");
2277 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002278 const char *name =
2279 signals->Attribute("name");
2280 if (name != nullptr)
2281 {
2282 signalsArray.push_back(
2283 {{"name", name},
2284 {"args", argsArray}});
2285 }
2286
Ed Tanous1abe55e2018-09-05 08:30:59 -07002287 signals =
2288 signals->NextSiblingElement(
2289 "signal");
2290 }
2291
Ed Tanous1abe55e2018-09-05 08:30:59 -07002292 break;
2293 }
2294
2295 interface = interface->NextSiblingElement(
2296 "interface");
2297 }
2298 if (interface == nullptr)
2299 {
2300 // if we got to the end of the list and
2301 // never found a match, throw 404
2302 res.result(
2303 boost::beast::http::status::not_found);
2304 }
2305 }
2306 }
2307 res.end();
2308 },
2309 processName, objectPath,
2310 "org.freedesktop.DBus.Introspectable", "Introspect");
2311 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002312 else
2313 {
2314 if (req.method() != "POST"_method)
2315 {
2316 res.result(boost::beast::http::status::not_found);
2317 res.end();
2318 return;
2319 }
2320
2321 nlohmann::json requestDbusData =
2322 nlohmann::json::parse(req.body, nullptr, false);
2323
2324 if (requestDbusData.is_discarded())
2325 {
2326 res.result(boost::beast::http::status::bad_request);
2327 res.end();
2328 return;
2329 }
2330 if (!requestDbusData.is_array())
2331 {
2332 res.result(boost::beast::http::status::bad_request);
2333 res.end();
2334 return;
2335 }
2336 auto transaction = std::make_shared<InProgressActionData>(res);
2337
2338 transaction->path = objectPath;
2339 transaction->methodName = methodName;
2340 transaction->arguments = std::move(requestDbusData);
2341
2342 findActionOnInterface(transaction, processName);
2343 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002344 });
2345}
2346} // namespace openbmc_mapper
2347} // namespace crow