blob: 6938079f23309298727257a70d4b673034f0220e [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -070016#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070024#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070025#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070026#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
30namespace openbmc_mapper
31{
Ed Tanousba9f9a62017-10-11 16:40:35 -070032
Matt Spinler3ae4ba72018-12-05 14:01:22 -060033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Matt Spinler2ae60092018-12-06 10:35:36 -060037const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060038const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060039const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060040const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060041const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler16caaee2019-01-15 11:40:34 -060042const std::string methodOutputFailedMsg = "500 Method Output Error";
Matt Spinler6db06242018-12-11 11:21:22 -060043
Matt Spinler2ae60092018-12-06 10:35:36 -060044const std::string notFoundDesc =
45 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060046const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060047const std::string noJsonDesc = "No JSON object could be decoded";
48const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060049const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060050const std::string forbiddenPropDesc =
51 "The specified property cannot be created";
52const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060053
54void setErrorResponse(crow::Response &res, boost::beast::http::status result,
55 const std::string &desc, const std::string &msg)
56{
57 res.result(result);
58 res.jsonValue = {{"data", {{"description", desc}}},
59 {"message", msg},
60 {"status", "error"}};
61}
62
Ed Tanouse3cb5a32018-08-08 14:16:49 -070063void introspectObjects(const std::string &processName,
64 const std::string &objectPath,
65 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070066{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070067 if (transaction->res.jsonValue.is_null())
68 {
69 transaction->res.jsonValue = {{"status", "ok"},
70 {"bus_name", processName},
71 {"objects", nlohmann::json::array()}};
72 }
73
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070075 [transaction, processName{std::string(processName)},
76 objectPath{std::string(objectPath)}](
77 const boost::system::error_code ec,
78 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 if (ec)
80 {
81 BMCWEB_LOG_ERROR
82 << "Introspect call failed with error: " << ec.message()
83 << " on process: " << processName << " path: " << objectPath
84 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070085 return;
86 }
87 transaction->res.jsonValue["objects"].push_back(
88 {{"path", objectPath}});
89
90 tinyxml2::XMLDocument doc;
91
92 doc.Parse(introspect_xml.c_str());
93 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
94 if (pRoot == nullptr)
95 {
96 BMCWEB_LOG_ERROR << "XML document failed to parse "
97 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070098 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 else
100 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700101 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
102 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700104 const char *childPath = node->Attribute("name");
105 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 std::string newpath;
108 if (objectPath != "/")
109 {
110 newpath += objectPath;
111 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700112 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700114 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700116
117 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 }
119 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700121 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700123}
Ed Tanous64530012018-02-06 17:08:16 -0800124
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600125void getPropertiesForEnumerate(const std::string &objectPath,
126 const std::string &service,
127 const std::string &interface,
128 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
129{
130 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
131 << service << " " << interface;
132
133 crow::connections::systemBus->async_method_call(
134 [asyncResp, objectPath, service,
135 interface](const boost::system::error_code ec,
136 const std::vector<
137 std::pair<std::string, dbus::utility::DbusVariantType>>
138 &propertiesList) {
139 if (ec)
140 {
141 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
142 << interface << " service " << service
143 << " failed with code " << ec;
144 return;
145 }
146
147 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
148 nlohmann::json &objectJson = dataJson[objectPath];
149 if (objectJson.is_null())
150 {
151 objectJson = nlohmann::json::object();
152 }
153
154 for (const auto &[name, value] : propertiesList)
155 {
156 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800157 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
158 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600159 }
160 },
161 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
162 interface);
163}
164
165// Find any results that weren't picked up by ObjectManagers, to be
166// called after all ObjectManagers are searched for and called.
167void findRemainingObjectsForEnumerate(
168 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
169 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
170{
171 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
172 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
173
174 for (const auto &[path, interface_map] : *subtree)
175 {
176 if (path == objectPath)
177 {
178 // An enumerate does not return the target path's properties
179 continue;
180 }
181 if (dataJson.find(path) == dataJson.end())
182 {
183 for (const auto &[service, interfaces] : interface_map)
184 {
185 for (const auto &interface : interfaces)
186 {
187 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
188 {
189 getPropertiesForEnumerate(path, service, interface,
190 asyncResp);
191 }
192 }
193 }
194 }
195 }
196}
197
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600198struct InProgressEnumerateData
199{
200 InProgressEnumerateData(const std::string &objectPath,
201 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
202 objectPath(objectPath),
203 asyncResp(asyncResp)
204 {
205 }
206
207 ~InProgressEnumerateData()
208 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600209 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600210 }
211
212 const std::string objectPath;
213 std::shared_ptr<GetSubTreeType> subtree;
214 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
215};
216
217void getManagedObjectsForEnumerate(
218 const std::string &object_name, const std::string &object_manager_path,
219 const std::string &connection_name,
220 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221{
Ed Tanous049a0512018-11-01 13:58:42 -0700222 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
223 << " object_manager_path " << object_manager_path
224 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600226 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700227 connection_name](const boost::system::error_code ec,
228 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 if (ec)
230 {
Ed Tanous049a0512018-11-01 13:58:42 -0700231 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600232 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700233 << " failed with code " << ec;
234 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700235 }
Ed Tanous64530012018-02-06 17:08:16 -0800236
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600237 nlohmann::json &dataJson =
238 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700239
240 for (const auto &objectPath : objects)
241 {
242 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 {
Ed Tanous049a0512018-11-01 13:58:42 -0700244 BMCWEB_LOG_DEBUG << "Reading object "
245 << objectPath.first.str;
246 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 if (objectJson.is_null())
248 {
249 objectJson = nlohmann::json::object();
250 }
251 for (const auto &interface : objectPath.second)
252 {
253 for (const auto &property : interface.second)
254 {
255 nlohmann::json &propertyJson =
256 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800257 std::visit([&propertyJson](
258 auto &&val) { propertyJson = val; },
259 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 }
261 }
262 }
Ed Tanous049a0512018-11-01 13:58:42 -0700263 for (const auto &interface : objectPath.second)
264 {
265 if (interface.first == "org.freedesktop.DBus.ObjectManager")
266 {
267 getManagedObjectsForEnumerate(
268 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600269 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700270 }
271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
273 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700274 connection_name, object_manager_path,
275 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
276}
277
278void findObjectManagerPathForEnumerate(
279 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600280 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700281{
Ed Tanous049a0512018-11-01 13:58:42 -0700282 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
283 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600285 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700286 const boost::system::error_code ec,
287 const boost::container::flat_map<
288 std::string, boost::container::flat_map<
289 std::string, std::vector<std::string>>>
290 &objects) {
291 if (ec)
292 {
Ed Tanous049a0512018-11-01 13:58:42 -0700293 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
294 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700295 return;
296 }
297
Ed Tanousf254ba72018-10-12 13:40:35 -0700298 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700299 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700300 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 {
302 if (connectionGroup.first == connection_name)
303 {
304 // Found the object manager path for this resource.
305 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700306 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600307 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700308 return;
309 }
310 }
311 }
312 },
313 "xyz.openbmc_project.ObjectMapper",
314 "/xyz/openbmc_project/object_mapper",
315 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
316 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700317}
Ed Tanous64530012018-02-06 17:08:16 -0800318
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600319// Uses GetObject to add the object info about the target /enumerate path to the
320// results of GetSubTree, as GetSubTree will not return info for the
321// target path, and then continues on enumerating the rest of the tree.
322void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
323{
324 using GetObjectType =
325 std::vector<std::pair<std::string, std::vector<std::string>>>;
326
327 crow::connections::systemBus->async_method_call(
328 [transaction](const boost::system::error_code ec,
329 const GetObjectType &objects) {
330 if (ec)
331 {
332 BMCWEB_LOG_ERROR << "GetObject for path "
333 << transaction->objectPath
334 << " failed with code " << ec;
335 return;
336 }
337
338 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
339 << " has " << objects.size() << " entries";
340 if (!objects.empty())
341 {
342 transaction->subtree->emplace_back(transaction->objectPath,
343 objects);
344 }
345
346 // Map indicating connection name, and the path where the object
347 // manager exists
348 boost::container::flat_map<std::string, std::string> connections;
349
350 for (const auto &object : *(transaction->subtree))
351 {
352 for (const auto &connection : object.second)
353 {
354 std::string &objectManagerPath =
355 connections[connection.first];
356 for (const auto &interface : connection.second)
357 {
358 BMCWEB_LOG_DEBUG << connection.first
359 << " has interface " << interface;
360 if (interface == "org.freedesktop.DBus.ObjectManager")
361 {
362 BMCWEB_LOG_DEBUG << "found object manager path "
363 << object.first;
364 objectManagerPath = object.first;
365 }
366 }
367 }
368 }
369 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
370
371 for (const auto &connection : connections)
372 {
373 // If we already know where the object manager is, we don't need
374 // to search for it, we can call directly in to
375 // getManagedObjects
376 if (!connection.second.empty())
377 {
378 getManagedObjectsForEnumerate(
379 transaction->objectPath, connection.second,
380 connection.first, transaction);
381 }
382 else
383 {
384 // otherwise we need to find the object manager path before
385 // we can continue
386 findObjectManagerPathForEnumerate(
387 transaction->objectPath, connection.first, transaction);
388 }
389 }
390 },
391 "xyz.openbmc_project.ObjectMapper",
392 "/xyz/openbmc_project/object_mapper",
393 "xyz.openbmc_project.ObjectMapper", "GetObject",
394 transaction->objectPath, std::array<const char *, 0>());
395}
Ed Tanous64530012018-02-06 17:08:16 -0800396
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700397// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398struct InProgressActionData
399{
400 InProgressActionData(crow::Response &res) : res(res){};
401 ~InProgressActionData()
402 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600403 // Methods could have been called across different owners
404 // and interfaces, where some calls failed and some passed.
405 //
406 // The rules for this are:
407 // * if no method was called - error
408 // * if a method failed and none passed - error
409 // (converse: if at least one method passed - OK)
410 // * for the method output:
411 // * if output processing didn't fail, return the data
412
413 // Only deal with method returns if nothing failed earlier
414 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600416 if (!methodPassed)
417 {
418 if (methodFailed)
419 {
420 setErrorResponse(res,
421 boost::beast::http::status::bad_request,
422 "Method call failed", methodFailedMsg);
423 }
424 else
425 {
426 setErrorResponse(res, boost::beast::http::status::not_found,
427 methodNotFoundDesc, notFoundMsg);
428 }
429 }
430 else
431 {
432 if (outputFailed)
433 {
434 setErrorResponse(
435 res, boost::beast::http::status::internal_server_error,
436 "Method output failure", methodOutputFailedMsg);
437 }
438 else
439 {
440 res.jsonValue = {{"status", "ok"},
441 {"message", "200 OK"},
442 {"data", methodResponse}};
443 }
444 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600446
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700448 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700449
Matt Spinler6db06242018-12-11 11:21:22 -0600450 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700451 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600452 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
453 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 }
455 crow::Response &res;
456 std::string path;
457 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600458 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600459 bool methodPassed = false;
460 bool methodFailed = false;
461 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600462 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600463 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700465};
466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467std::vector<std::string> dbusArgSplit(const std::string &string)
468{
469 std::vector<std::string> ret;
470 if (string.empty())
471 {
472 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700473 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700474 ret.push_back("");
475 int containerDepth = 0;
476
477 for (std::string::const_iterator character = string.begin();
478 character != string.end(); character++)
479 {
480 ret.back() += *character;
481 switch (*character)
482 {
483 case ('a'):
484 break;
485 case ('('):
486 case ('{'):
487 containerDepth++;
488 break;
489 case ('}'):
490 case (')'):
491 containerDepth--;
492 if (containerDepth == 0)
493 {
494 if (character + 1 != string.end())
495 {
496 ret.push_back("");
497 }
498 }
499 break;
500 default:
501 if (containerDepth == 0)
502 {
503 if (character + 1 != string.end())
504 {
505 ret.push_back("");
506 }
507 }
508 break;
509 }
510 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600511
512 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700513}
514
Ed Tanousd76323e2018-08-07 14:35:40 -0700515int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 const nlohmann::json &input_json)
517{
518 int r = 0;
519 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
520 << " to type: " << arg_type;
521 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700522
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 // Assume a single object for now.
524 const nlohmann::json *j = &input_json;
525 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700526
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700527 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 {
529 // If we are decoding multiple objects, grab the pointer to the
530 // iterator, and increment it for the next loop
531 if (argTypes.size() > 1)
532 {
533 if (jIt == input_json.end())
534 {
535 return -2;
536 }
537 j = &*jIt;
538 jIt++;
539 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700540 const int64_t *intValue = j->get_ptr<const int64_t *>();
541 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
542 const std::string *stringValue = j->get_ptr<const std::string *>();
543 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544 const bool *b = j->get_ptr<const bool *>();
545 int64_t v = 0;
546 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700547
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548 // Do some basic type conversions that make sense. uint can be
549 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 v = static_cast<int64_t>(*uintValue);
553 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700555 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700557 d = static_cast<double>(*uintValue);
558 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700560 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700562 d = static_cast<double>(*intValue);
563 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700564 }
565
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700566 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
570 return -1;
571 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000572 r = sd_bus_message_append_basic(m, argCode[0],
573 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 if (r < 0)
575 {
576 return r;
577 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700578 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700579 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
583 return -1;
584 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700585 int32_t i = static_cast<int32_t>(*intValue);
586 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 if (r < 0)
588 {
589 return r;
590 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700591 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593 {
594 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 int boolInt = false;
596 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700598 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 }
600 else if (b != nullptr)
601 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600602 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700604 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 }
608 else
609 {
610 return -1;
611 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700612 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 if (r < 0)
614 {
615 return r;
616 }
617 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700618 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
622 return -1;
623 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700624 int16_t n = static_cast<int16_t>(*intValue);
625 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 if (r < 0)
627 {
628 return r;
629 }
630 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700631 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700633 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 {
635 return -1;
636 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 if (r < 0)
639 {
640 return r;
641 }
642 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700643 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700645 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 {
647 return -1;
648 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 uint8_t y = static_cast<uint8_t>(*uintValue);
650 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700651 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700652 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
656 return -1;
657 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 uint16_t q = static_cast<uint16_t>(*uintValue);
659 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700663 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 {
665 return -1;
666 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700667 uint32_t u = static_cast<uint32_t>(*uintValue);
668 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700670 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 {
674 return -1;
675 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700676 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700678 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 if (r < 0)
688 {
689 return r;
690 }
691
692 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
693 ++it)
694 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700695 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 if (r < 0)
697 {
698 return r;
699 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 }
701 sd_bus_message_close_container(m);
702 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700703 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700705 std::string containedType = argCode.substr(1);
706 BMCWEB_LOG_DEBUG << "variant type: " << argCode
707 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700709 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 if (r < 0)
711 {
712 return r;
713 }
714
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700715 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 if (r < 0)
717 {
718 return r;
719 }
720
721 r = sd_bus_message_close_container(m);
722 if (r < 0)
723 {
724 return r;
725 }
726 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700727 else if (boost::starts_with(argCode, "(") &&
728 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700730 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700732 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800733 if (r < 0)
734 {
735 return r;
736 }
737
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000739 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 {
741 if (it == j->end())
742 {
743 return -1;
744 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000745 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 if (r < 0)
747 {
748 return r;
749 }
750 it++;
751 }
752 r = sd_bus_message_close_container(m);
753 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700754 else if (boost::starts_with(argCode, "{") &&
755 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700757 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800760 if (r < 0)
761 {
762 return r;
763 }
764
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700765 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 if (codes.size() != 2)
767 {
768 return -1;
769 }
770 const std::string &key_type = codes[0];
771 const std::string &value_type = codes[1];
772 for (auto it : j->items())
773 {
774 r = convertJsonToDbus(m, key_type, it.key());
775 if (r < 0)
776 {
777 return r;
778 }
779
780 r = convertJsonToDbus(m, value_type, it.value());
781 if (r < 0)
782 {
783 return r;
784 }
785 }
786 r = sd_bus_message_close_container(m);
787 }
788 else
789 {
790 return -2;
791 }
792 if (r < 0)
793 {
794 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700795 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700796
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 if (argTypes.size() > 1)
798 {
799 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700800 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700801 }
Matt Spinler127ea542019-01-14 11:04:28 -0600802
803 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700804}
805
Matt Spinlerd22a7132019-01-14 12:14:30 -0600806template <typename T>
807int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
808 nlohmann::json &data)
809{
810 T value;
811
812 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
813 if (r < 0)
814 {
815 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
816 << " failed!";
817 return r;
818 }
819
820 data = value;
821 return 0;
822}
823
Matt Spinler16caaee2019-01-15 11:40:34 -0600824int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600825 sdbusplus::message::message &m, nlohmann::json &response);
826
827int readDictEntryFromMessage(const std::string &typeCode,
828 sdbusplus::message::message &m,
829 nlohmann::json &object)
830{
831 std::vector<std::string> types = dbusArgSplit(typeCode);
832 if (types.size() != 2)
833 {
834 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
835 << types.size();
836 return -1;
837 }
838
839 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
840 typeCode.c_str());
841 if (r < 0)
842 {
843 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
844 return r;
845 }
846
847 nlohmann::json key;
848 r = convertDBusToJSON(types[0], m, key);
849 if (r < 0)
850 {
851 return r;
852 }
853
854 const std::string *keyPtr = key.get_ptr<const std::string *>();
855 if (keyPtr == nullptr)
856 {
857 // json doesn't support non-string keys. If we hit this condition,
858 // convert the result to a string so we can proceed
859 key = key.dump();
860 keyPtr = key.get_ptr<const std::string *>();
861 // in theory this can't fail now, but lets be paranoid about it anyway
862 if (keyPtr == nullptr)
863 {
864 return -1;
865 }
866 }
867 nlohmann::json &value = object[*keyPtr];
868
869 r = convertDBusToJSON(types[1], m, value);
870 if (r < 0)
871 {
872 return r;
873 }
874
875 r = sd_bus_message_exit_container(m.get());
876 if (r < 0)
877 {
878 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
879 return r;
880 }
881
882 return 0;
883}
884
885int readArrayFromMessage(const std::string &typeCode,
886 sdbusplus::message::message &m, nlohmann::json &data)
887{
888 if (typeCode.size() < 2)
889 {
890 BMCWEB_LOG_ERROR << "Type code " << typeCode
891 << " too small for an array";
892 return -1;
893 }
894
895 std::string containedType = typeCode.substr(1);
896
897 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
898 containedType.c_str());
899 if (r < 0)
900 {
901 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
902 << r;
903 return r;
904 }
905
906 bool dict = boost::starts_with(containedType, "{") &&
907 boost::ends_with(containedType, "}");
908
909 if (dict)
910 {
911 // Remove the { }
912 containedType = containedType.substr(1, containedType.size() - 2);
913 data = nlohmann::json::object();
914 }
915 else
916 {
917 data = nlohmann::json::array();
918 }
919
920 while (true)
921 {
922 r = sd_bus_message_at_end(m.get(), false);
923 if (r < 0)
924 {
925 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
926 return r;
927 }
928
929 if (r > 0)
930 {
931 break;
932 }
933
934 // Dictionaries are only ever seen in an array
935 if (dict)
936 {
937 r = readDictEntryFromMessage(containedType, m, data);
938 if (r < 0)
939 {
940 return r;
941 }
942 }
943 else
944 {
945 data.push_back(nlohmann::json());
946
947 r = convertDBusToJSON(containedType, m, data.back());
948 if (r < 0)
949 {
950 return r;
951 }
952 }
953 }
954
955 r = sd_bus_message_exit_container(m.get());
956 if (r < 0)
957 {
958 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
959 return r;
960 }
961
962 return 0;
963}
964
Matt Spinler75c6c672019-01-14 13:01:46 -0600965int readStructFromMessage(const std::string &typeCode,
966 sdbusplus::message::message &m, nlohmann::json &data)
967{
968 if (typeCode.size() < 3)
969 {
970 BMCWEB_LOG_ERROR << "Type code " << typeCode
971 << " too small for a struct";
972 return -1;
973 }
974
975 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
976 std::vector<std::string> types = dbusArgSplit(containedTypes);
977
978 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
979 containedTypes.c_str());
980 if (r < 0)
981 {
982 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
983 << r;
984 return r;
985 }
986
987 for (const std::string &type : types)
988 {
989 data.push_back(nlohmann::json());
990 r = convertDBusToJSON(type, m, data.back());
991 if (r < 0)
992 {
993 return r;
994 }
995 }
996
997 r = sd_bus_message_exit_container(m.get());
998 if (r < 0)
999 {
1000 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1001 return r;
1002 }
1003 return 0;
1004}
1005
Matt Spinler89c19702019-01-14 13:13:00 -06001006int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1007{
1008 const char *containerType;
1009 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1010 if (r < 0)
1011 {
1012 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1013 return r;
1014 }
1015
1016 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1017 containerType);
1018 if (r < 0)
1019 {
1020 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1021 << r;
1022 return r;
1023 }
1024
1025 r = convertDBusToJSON(containerType, m, data);
1026 if (r < 0)
1027 {
1028 return r;
1029 }
1030
1031 r = sd_bus_message_exit_container(m.get());
1032 if (r < 0)
1033 {
1034 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1035 return r;
1036 }
1037
1038 return 0;
1039}
1040
Matt Spinler6df8f992019-01-14 12:47:47 -06001041int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001042 sdbusplus::message::message &m, nlohmann::json &response)
1043{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001044 int r = 0;
1045 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1046
Matt Spinlerd22a7132019-01-14 12:14:30 -06001047 for (const std::string &typeCode : returnTypes)
1048 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001049 nlohmann::json *thisElement = &response;
1050 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001051 {
1052 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001053 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001054 }
1055
1056 if (typeCode == "s")
1057 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001058 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001059 if (r < 0)
1060 {
1061 return r;
1062 }
1063 }
1064 else if (typeCode == "g")
1065 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001066 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001067 if (r < 0)
1068 {
1069 return r;
1070 }
1071 }
1072 else if (typeCode == "o")
1073 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001074 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001075 if (r < 0)
1076 {
1077 return r;
1078 }
1079 }
1080 else if (typeCode == "b")
1081 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001082 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001083 if (r < 0)
1084 {
1085 return r;
1086 }
1087
Matt Spinlerf39420c2019-01-30 12:57:18 -06001088 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001089 }
1090 else if (typeCode == "u")
1091 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001092 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001093 if (r < 0)
1094 {
1095 return r;
1096 }
1097 }
1098 else if (typeCode == "i")
1099 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001100 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001101 if (r < 0)
1102 {
1103 return r;
1104 }
1105 }
1106 else if (typeCode == "x")
1107 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001108 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001109 if (r < 0)
1110 {
1111 return r;
1112 }
1113 }
1114 else if (typeCode == "t")
1115 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001116 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001117 if (r < 0)
1118 {
1119 return r;
1120 }
1121 }
1122 else if (typeCode == "n")
1123 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001124 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001125 if (r < 0)
1126 {
1127 return r;
1128 }
1129 }
1130 else if (typeCode == "q")
1131 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001132 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001133 if (r < 0)
1134 {
1135 return r;
1136 }
1137 }
1138 else if (typeCode == "y")
1139 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001140 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001141 if (r < 0)
1142 {
1143 return r;
1144 }
1145 }
1146 else if (typeCode == "d")
1147 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001148 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001149 if (r < 0)
1150 {
1151 return r;
1152 }
1153 }
1154 else if (typeCode == "h")
1155 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001156 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001157 if (r < 0)
1158 {
1159 return r;
1160 }
1161 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001162 else if (boost::starts_with(typeCode, "a"))
1163 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001164 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001165 if (r < 0)
1166 {
1167 return r;
1168 }
1169 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001170 else if (boost::starts_with(typeCode, "(") &&
1171 boost::ends_with(typeCode, ")"))
1172 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001173 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001174 if (r < 0)
1175 {
1176 return r;
1177 }
1178 }
Matt Spinler89c19702019-01-14 13:13:00 -06001179 else if (boost::starts_with(typeCode, "v"))
1180 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001181 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001182 if (r < 0)
1183 {
1184 return r;
1185 }
1186 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001187 else
1188 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001189 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1190 return -2;
1191 }
1192 }
1193
Matt Spinler16caaee2019-01-15 11:40:34 -06001194 return 0;
1195}
1196
1197void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1198 sdbusplus::message::message &m,
1199 const std::string &returnType)
1200{
Matt Spinler39a4e392019-01-15 11:53:13 -06001201 nlohmann::json data;
1202
1203 int r = convertDBusToJSON(returnType, m, data);
1204 if (r < 0)
1205 {
1206 transaction->outputFailed = true;
1207 return;
1208 }
1209
1210 if (data.is_null())
1211 {
1212 return;
1213 }
1214
1215 if (transaction->methodResponse.is_null())
1216 {
1217 transaction->methodResponse = std::move(data);
1218 return;
1219 }
1220
1221 // If they're both dictionaries or arrays, merge into one.
1222 // Otherwise, make the results an array with every result
1223 // an entry. Could also just fail in that case, but it
1224 // seems better to get the data back somehow.
1225
1226 if (transaction->methodResponse.is_object() && data.is_object())
1227 {
1228 for (const auto &obj : data.items())
1229 {
1230 // Note: Will overwrite the data for a duplicate key
1231 transaction->methodResponse.emplace(obj.key(),
1232 std::move(obj.value()));
1233 }
1234 return;
1235 }
1236
1237 if (transaction->methodResponse.is_array() && data.is_array())
1238 {
1239 for (auto &obj : data)
1240 {
1241 transaction->methodResponse.push_back(std::move(obj));
1242 }
1243 return;
1244 }
1245
1246 if (!transaction->convertedToArray)
1247 {
1248 // They are different types. May as well turn them into an array
1249 nlohmann::json j = std::move(transaction->methodResponse);
1250 transaction->methodResponse = nlohmann::json::array();
1251 transaction->methodResponse.push_back(std::move(j));
1252 transaction->methodResponse.push_back(std::move(data));
1253 transaction->convertedToArray = true;
1254 }
1255 else
1256 {
1257 transaction->methodResponse.push_back(std::move(data));
1258 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001259}
1260
Ed Tanousd76323e2018-08-07 14:35:40 -07001261void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001262 const std::string &connectionName)
1263{
1264 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1265 << connectionName;
1266 crow::connections::systemBus->async_method_call(
1267 [transaction, connectionName{std::string(connectionName)}](
1268 const boost::system::error_code ec,
1269 const std::string &introspect_xml) {
1270 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1271 if (ec)
1272 {
1273 BMCWEB_LOG_ERROR
1274 << "Introspect call failed with error: " << ec.message()
1275 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001276 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001277 }
Matt Spinler318bd892019-01-15 09:59:20 -06001278 tinyxml2::XMLDocument doc;
1279
1280 doc.Parse(introspect_xml.data(), introspect_xml.size());
1281 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1282 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001283 {
Matt Spinler318bd892019-01-15 09:59:20 -06001284 BMCWEB_LOG_ERROR << "XML document failed to parse "
1285 << connectionName << "\n";
1286 return;
1287 }
1288 tinyxml2::XMLElement *interfaceNode =
1289 pRoot->FirstChildElement("interface");
1290 while (interfaceNode != nullptr)
1291 {
1292 const char *thisInterfaceName =
1293 interfaceNode->Attribute("name");
1294 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001295 {
Matt Spinler318bd892019-01-15 09:59:20 -06001296 if (!transaction->interfaceName.empty() &&
1297 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 {
Matt Spinler318bd892019-01-15 09:59:20 -06001299 interfaceNode =
1300 interfaceNode->NextSiblingElement("interface");
1301 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001302 }
Matt Spinler318bd892019-01-15 09:59:20 -06001303
1304 tinyxml2::XMLElement *methodNode =
1305 interfaceNode->FirstChildElement("method");
1306 while (methodNode != nullptr)
1307 {
1308 const char *thisMethodName =
1309 methodNode->Attribute("name");
1310 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1311 if (thisMethodName != nullptr &&
1312 thisMethodName == transaction->methodName)
1313 {
1314 BMCWEB_LOG_DEBUG
1315 << "Found method named " << thisMethodName
1316 << " on interface " << thisInterfaceName;
1317 sdbusplus::message::message m =
1318 crow::connections::systemBus->new_method_call(
1319 connectionName.c_str(),
1320 transaction->path.c_str(),
1321 thisInterfaceName,
1322 transaction->methodName.c_str());
1323
1324 tinyxml2::XMLElement *argumentNode =
1325 methodNode->FirstChildElement("arg");
1326
Matt Spinler16caaee2019-01-15 11:40:34 -06001327 std::string returnType;
1328
1329 // Find the output type
1330 while (argumentNode != nullptr)
1331 {
1332 const char *argDirection =
1333 argumentNode->Attribute("direction");
1334 const char *argType =
1335 argumentNode->Attribute("type");
1336 if (argDirection != nullptr &&
1337 argType != nullptr &&
1338 std::string(argDirection) == "out")
1339 {
1340 returnType = argType;
1341 break;
1342 }
1343 argumentNode =
1344 argumentNode->NextSiblingElement("arg");
1345 }
1346
Matt Spinler318bd892019-01-15 09:59:20 -06001347 nlohmann::json::const_iterator argIt =
1348 transaction->arguments.begin();
1349
Matt Spinler16caaee2019-01-15 11:40:34 -06001350 argumentNode = methodNode->FirstChildElement("arg");
1351
Matt Spinler318bd892019-01-15 09:59:20 -06001352 while (argumentNode != nullptr)
1353 {
1354 const char *argDirection =
1355 argumentNode->Attribute("direction");
1356 const char *argType =
1357 argumentNode->Attribute("type");
1358 if (argDirection != nullptr &&
1359 argType != nullptr &&
1360 std::string(argDirection) == "in")
1361 {
1362 if (argIt == transaction->arguments.end())
1363 {
1364 transaction->setErrorStatus(
1365 "Invalid method args");
1366 return;
1367 }
1368 if (convertJsonToDbus(m.get(),
1369 std::string(argType),
1370 *argIt) < 0)
1371 {
1372 transaction->setErrorStatus(
1373 "Invalid method arg type");
1374 return;
1375 }
1376
1377 argIt++;
1378 }
1379 argumentNode =
1380 argumentNode->NextSiblingElement("arg");
1381 }
1382
1383 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001384 m, [transaction, returnType](
1385 boost::system::error_code ec,
1386 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001387 if (ec)
1388 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001389 transaction->methodFailed = true;
Matt Spinler318bd892019-01-15 09:59:20 -06001390 return;
1391 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001392 else
1393 {
1394 transaction->methodPassed = true;
1395 }
1396
1397 handleMethodResponse(transaction, m,
1398 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001399 });
1400 break;
1401 }
1402 methodNode = methodNode->NextSiblingElement("method");
1403 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001404 }
Matt Spinler318bd892019-01-15 09:59:20 -06001405 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001406 }
1407 },
1408 connectionName, transaction->path,
1409 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001410}
1411
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001412void handleAction(const crow::Request &req, crow::Response &res,
1413 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001414{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001415 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1416 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001417 nlohmann::json requestDbusData =
1418 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001419
Ed Tanous1abe55e2018-09-05 08:30:59 -07001420 if (requestDbusData.is_discarded())
1421 {
Matt Spinler6db06242018-12-11 11:21:22 -06001422 setErrorResponse(res, boost::beast::http::status::bad_request,
1423 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001424 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001425 return;
1426 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001427 nlohmann::json::iterator data = requestDbusData.find("data");
1428 if (data == requestDbusData.end())
1429 {
Matt Spinler6db06242018-12-11 11:21:22 -06001430 setErrorResponse(res, boost::beast::http::status::bad_request,
1431 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001432 res.end();
1433 return;
1434 }
1435
1436 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001437 {
Matt Spinler6db06242018-12-11 11:21:22 -06001438 setErrorResponse(res, boost::beast::http::status::bad_request,
1439 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001440 res.end();
1441 return;
1442 }
1443 auto transaction = std::make_shared<InProgressActionData>(res);
1444
1445 transaction->path = objectPath;
1446 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001447 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001448 crow::connections::systemBus->async_method_call(
1449 [transaction](
1450 const boost::system::error_code ec,
1451 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001452 &interfaceNames) {
1453 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001454 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001455 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001456 setErrorResponse(transaction->res,
1457 boost::beast::http::status::not_found,
1458 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001459 return;
1460 }
1461
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001462 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1463 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001464
1465 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001466 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001467 {
1468 findActionOnInterface(transaction, object.first);
1469 }
1470 },
1471 "xyz.openbmc_project.ObjectMapper",
1472 "/xyz/openbmc_project/object_mapper",
1473 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1474 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001475}
1476
Matt Spinlerde818812018-12-11 16:39:20 -06001477void handleDelete(const crow::Request &req, crow::Response &res,
1478 const std::string &objectPath)
1479{
1480 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1481
1482 crow::connections::systemBus->async_method_call(
1483 [&res, objectPath](
1484 const boost::system::error_code ec,
1485 const std::vector<std::pair<std::string, std::vector<std::string>>>
1486 &interfaceNames) {
1487 if (ec || interfaceNames.size() <= 0)
1488 {
1489 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001490 setErrorResponse(res,
1491 boost::beast::http::status::method_not_allowed,
1492 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001493 res.end();
1494 return;
1495 }
1496
1497 auto transaction = std::make_shared<InProgressActionData>(res);
1498 transaction->path = objectPath;
1499 transaction->methodName = "Delete";
1500 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1501
1502 for (const std::pair<std::string, std::vector<std::string>>
1503 &object : interfaceNames)
1504 {
1505 findActionOnInterface(transaction, object.first);
1506 }
1507 },
1508 "xyz.openbmc_project.ObjectMapper",
1509 "/xyz/openbmc_project/object_mapper",
1510 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1511 std::array<const char *, 0>());
1512}
1513
Ed Tanousf839dfe2018-11-12 11:11:15 -08001514void handleList(crow::Response &res, const std::string &objectPath,
1515 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001516{
1517 crow::connections::systemBus->async_method_call(
1518 [&res](const boost::system::error_code ec,
1519 std::vector<std::string> &objectPaths) {
1520 if (ec)
1521 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001522 setErrorResponse(res, boost::beast::http::status::not_found,
1523 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001524 }
1525 else
1526 {
1527 res.jsonValue = {{"status", "ok"},
1528 {"message", "200 OK"},
1529 {"data", std::move(objectPaths)}};
1530 }
1531 res.end();
1532 },
1533 "xyz.openbmc_project.ObjectMapper",
1534 "/xyz/openbmc_project/object_mapper",
1535 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001536 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001537}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001538
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001539void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001540{
Ed Tanous049a0512018-11-01 13:58:42 -07001541 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1542 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1543
1544 asyncResp->res.jsonValue = {{"message", "200 OK"},
1545 {"status", "ok"},
1546 {"data", nlohmann::json::object()}};
1547
Ed Tanous1abe55e2018-09-05 08:30:59 -07001548 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001549 [objectPath, asyncResp](const boost::system::error_code ec,
1550 GetSubTreeType &object_names) {
1551 auto transaction = std::make_shared<InProgressEnumerateData>(
1552 objectPath, asyncResp);
1553
1554 transaction->subtree =
1555 std::make_shared<GetSubTreeType>(std::move(object_names));
1556
Ed Tanous1abe55e2018-09-05 08:30:59 -07001557 if (ec)
1558 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001559 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1560 << transaction->objectPath;
1561 setErrorResponse(transaction->asyncResp->res,
1562 boost::beast::http::status::not_found,
1563 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001564 return;
1565 }
Ed Tanous64530012018-02-06 17:08:16 -08001566
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001567 // Add the data for the path passed in to the results
1568 // as if GetSubTree returned it, and continue on enumerating
1569 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001570 },
1571 "xyz.openbmc_project.ObjectMapper",
1572 "/xyz/openbmc_project/object_mapper",
1573 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001574 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001575}
Ed Tanous911ac312017-08-15 09:37:42 -07001576
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001577void handleGet(crow::Response &res, std::string &objectPath,
1578 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001579{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001580 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1581 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001582 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001583
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584 std::shared_ptr<std::string> path =
1585 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001586
Ed Tanous1abe55e2018-09-05 08:30:59 -07001587 using GetObjectType =
1588 std::vector<std::pair<std::string, std::vector<std::string>>>;
1589 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001590 [&res, path, propertyName](const boost::system::error_code ec,
1591 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001592 if (ec || object_names.size() <= 0)
1593 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001594 setErrorResponse(res, boost::beast::http::status::not_found,
1595 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001596 res.end();
1597 return;
1598 }
1599 std::shared_ptr<nlohmann::json> response =
1600 std::make_shared<nlohmann::json>(nlohmann::json::object());
1601 // The mapper should never give us an empty interface names list,
1602 // but check anyway
1603 for (const std::pair<std::string, std::vector<std::string>>
1604 connection : object_names)
1605 {
1606 const std::vector<std::string> &interfaceNames =
1607 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001608
Ed Tanous1abe55e2018-09-05 08:30:59 -07001609 if (interfaceNames.size() <= 0)
1610 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001611 setErrorResponse(res, boost::beast::http::status::not_found,
1612 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001613 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001614 return;
1615 }
1616
1617 for (const std::string &interface : interfaceNames)
1618 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001619 sdbusplus::message::message m =
1620 crow::connections::systemBus->new_method_call(
1621 connection.first.c_str(), path->c_str(),
1622 "org.freedesktop.DBus.Properties", "GetAll");
1623 m.append(interface);
1624 crow::connections::systemBus->async_send(
1625 m, [&res, response,
1626 propertyName](const boost::system::error_code ec,
1627 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001628 if (ec)
1629 {
1630 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1631 << ec;
1632 }
1633 else
1634 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001635 nlohmann::json properties;
1636 int r =
1637 convertDBusToJSON("a{sv}", msg, properties);
1638 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001639 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001640 BMCWEB_LOG_ERROR
1641 << "convertDBusToJSON failed";
1642 }
1643 else
1644 {
1645 for (auto &prop : properties.items())
1646 {
1647 // if property name is empty, or matches
1648 // our search query, add it to the
1649 // response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001650
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001651 if (propertyName->empty())
1652 {
1653 (*response)[prop.key()] =
1654 std::move(prop.value());
1655 }
1656 else if (prop.key() == *propertyName)
1657 {
1658 *response = std::move(prop.value());
1659 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001660 }
1661 }
1662 }
1663 if (response.use_count() == 1)
1664 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001665 if (!propertyName->empty() && response->empty())
1666 {
1667 setErrorResponse(
1668 res,
1669 boost::beast::http::status::not_found,
1670 propNotFoundDesc, notFoundMsg);
1671 }
1672 else
1673 {
1674 res.jsonValue = {{"status", "ok"},
1675 {"message", "200 OK"},
1676 {"data", *response}};
1677 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001678 res.end();
1679 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001680 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001681 }
1682 }
1683 },
1684 "xyz.openbmc_project.ObjectMapper",
1685 "/xyz/openbmc_project/object_mapper",
1686 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1687 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001688}
1689
Ed Tanous1abe55e2018-09-05 08:30:59 -07001690struct AsyncPutRequest
1691{
1692 AsyncPutRequest(crow::Response &res) : res(res)
1693 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001694 }
1695 ~AsyncPutRequest()
1696 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001697 if (res.jsonValue.empty())
1698 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001699 setErrorResponse(res, boost::beast::http::status::forbidden,
1700 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001701 }
1702
1703 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001704 }
1705
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001706 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001707 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001708 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1709 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001710 }
1711
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 crow::Response &res;
1713 std::string objectPath;
1714 std::string propertyName;
1715 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001716};
1717
Ed Tanousd76323e2018-08-07 14:35:40 -07001718void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001719 const std::string &objectPath, const std::string &destProperty)
1720{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001721 if (destProperty.empty())
1722 {
1723 setErrorResponse(res, boost::beast::http::status::forbidden,
1724 forbiddenResDesc, forbiddenMsg);
1725 res.end();
1726 return;
1727 }
1728
Ed Tanous1abe55e2018-09-05 08:30:59 -07001729 nlohmann::json requestDbusData =
1730 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001731
Ed Tanous1abe55e2018-09-05 08:30:59 -07001732 if (requestDbusData.is_discarded())
1733 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001734 setErrorResponse(res, boost::beast::http::status::bad_request,
1735 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001736 res.end();
1737 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001738 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001739
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1741 if (propertyIt == requestDbusData.end())
1742 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001743 setErrorResponse(res, boost::beast::http::status::bad_request,
1744 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001745 res.end();
1746 return;
1747 }
1748 const nlohmann::json &propertySetValue = *propertyIt;
1749 auto transaction = std::make_shared<AsyncPutRequest>(res);
1750 transaction->objectPath = objectPath;
1751 transaction->propertyName = destProperty;
1752 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001753
Ed Tanous1abe55e2018-09-05 08:30:59 -07001754 using GetObjectType =
1755 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001756
Ed Tanous1abe55e2018-09-05 08:30:59 -07001757 crow::connections::systemBus->async_method_call(
1758 [transaction](const boost::system::error_code ec,
1759 const GetObjectType &object_names) {
1760 if (!ec && object_names.size() <= 0)
1761 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001762 setErrorResponse(transaction->res,
1763 boost::beast::http::status::not_found,
1764 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001765 return;
1766 }
Ed Tanous911ac312017-08-15 09:37:42 -07001767
Ed Tanous1abe55e2018-09-05 08:30:59 -07001768 for (const std::pair<std::string, std::vector<std::string>>
1769 connection : object_names)
1770 {
1771 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001772
Ed Tanous1abe55e2018-09-05 08:30:59 -07001773 crow::connections::systemBus->async_method_call(
1774 [connectionName{std::string(connectionName)},
1775 transaction](const boost::system::error_code ec,
1776 const std::string &introspectXml) {
1777 if (ec)
1778 {
1779 BMCWEB_LOG_ERROR
1780 << "Introspect call failed with error: "
1781 << ec.message()
1782 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001783 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001784 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001785 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001786 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001787
Ed Tanous1abe55e2018-09-05 08:30:59 -07001788 doc.Parse(introspectXml.c_str());
1789 tinyxml2::XMLNode *pRoot =
1790 doc.FirstChildElement("node");
1791 if (pRoot == nullptr)
1792 {
1793 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1794 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001795 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001796 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001797 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001798 tinyxml2::XMLElement *ifaceNode =
1799 pRoot->FirstChildElement("interface");
1800 while (ifaceNode != nullptr)
1801 {
1802 const char *interfaceName =
1803 ifaceNode->Attribute("name");
1804 BMCWEB_LOG_DEBUG << "found interface "
1805 << interfaceName;
1806 tinyxml2::XMLElement *propNode =
1807 ifaceNode->FirstChildElement("property");
1808 while (propNode != nullptr)
1809 {
1810 const char *propertyName =
1811 propNode->Attribute("name");
1812 BMCWEB_LOG_DEBUG << "Found property "
1813 << propertyName;
1814 if (propertyName == transaction->propertyName)
1815 {
1816 const char *argType =
1817 propNode->Attribute("type");
1818 if (argType != nullptr)
1819 {
1820 sdbusplus::message::message m =
1821 crow::connections::systemBus
1822 ->new_method_call(
1823 connectionName.c_str(),
1824 transaction->objectPath
1825 .c_str(),
1826 "org.freedesktop.DBus."
1827 "Properties",
1828 "Set");
1829 m.append(interfaceName,
1830 transaction->propertyName);
1831 int r = sd_bus_message_open_container(
1832 m.get(), SD_BUS_TYPE_VARIANT,
1833 argType);
1834 if (r < 0)
1835 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001836 transaction->setErrorStatus(
1837 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001838 return;
1839 }
1840 r = convertJsonToDbus(
1841 m.get(), argType,
1842 transaction->propertyValue);
1843 if (r < 0)
1844 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001845 transaction->setErrorStatus(
1846 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001847 return;
1848 }
1849 r = sd_bus_message_close_container(
1850 m.get());
1851 if (r < 0)
1852 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001853 transaction->setErrorStatus(
1854 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001855 return;
1856 }
Ed Tanous911ac312017-08-15 09:37:42 -07001857
Ed Tanous1abe55e2018-09-05 08:30:59 -07001858 crow::connections::systemBus
1859 ->async_send(
1860 m,
1861 [transaction](
1862 boost::system::error_code
1863 ec,
1864 sdbusplus::message::message
1865 &m) {
1866 BMCWEB_LOG_DEBUG << "sent";
1867 if (ec)
1868 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001869 setErrorResponse(
1870 transaction->res,
1871 boost::beast::http::
1872 status::
1873 forbidden,
1874 forbiddenPropDesc,
1875 ec.message());
1876 }
1877 else
1878 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001879 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001880 .jsonValue = {
1881 {"status", "ok"},
1882 {"message",
1883 "200 OK"},
1884 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001885 }
1886 });
1887 }
1888 }
1889 propNode =
1890 propNode->NextSiblingElement("property");
1891 }
1892 ifaceNode =
1893 ifaceNode->NextSiblingElement("interface");
1894 }
1895 },
1896 connectionName, transaction->objectPath,
1897 "org.freedesktop.DBus.Introspectable", "Introspect");
1898 }
1899 },
1900 "xyz.openbmc_project.ObjectMapper",
1901 "/xyz/openbmc_project/object_mapper",
1902 "xyz.openbmc_project.ObjectMapper", "GetObject",
1903 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001904}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001905
Ed Tanous049a0512018-11-01 13:58:42 -07001906inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1907 std::string &objectPath)
1908{
Ed Tanous049a0512018-11-01 13:58:42 -07001909
1910 // If accessing a single attribute, fill in and update objectPath,
1911 // otherwise leave destProperty blank
1912 std::string destProperty = "";
1913 const char *attrSeperator = "/attr/";
1914 size_t attrPosition = objectPath.find(attrSeperator);
1915 if (attrPosition != objectPath.npos)
1916 {
1917 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1918 objectPath.length());
1919 objectPath = objectPath.substr(0, attrPosition);
1920 }
1921
1922 if (req.method() == "POST"_method)
1923 {
1924 constexpr const char *actionSeperator = "/action/";
1925 size_t actionPosition = objectPath.find(actionSeperator);
1926 if (actionPosition != objectPath.npos)
1927 {
1928 std::string postProperty =
1929 objectPath.substr((actionPosition + strlen(actionSeperator)),
1930 objectPath.length());
1931 objectPath = objectPath.substr(0, actionPosition);
1932 handleAction(req, res, objectPath, postProperty);
1933 return;
1934 }
1935 }
1936 else if (req.method() == "GET"_method)
1937 {
1938 if (boost::ends_with(objectPath, "/enumerate"))
1939 {
1940 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1941 objectPath.end());
1942 handleEnumerate(res, objectPath);
1943 }
1944 else if (boost::ends_with(objectPath, "/list"))
1945 {
1946 objectPath.erase(objectPath.end() - sizeof("list"),
1947 objectPath.end());
1948 handleList(res, objectPath);
1949 }
1950 else
1951 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001952 // Trim any trailing "/" at the end
1953 if (boost::ends_with(objectPath, "/"))
1954 {
1955 objectPath.pop_back();
1956 handleList(res, objectPath, 1);
1957 }
1958 else
1959 {
1960 handleGet(res, objectPath, destProperty);
1961 }
Ed Tanous049a0512018-11-01 13:58:42 -07001962 }
1963 return;
1964 }
1965 else if (req.method() == "PUT"_method)
1966 {
1967 handlePut(req, res, objectPath, destProperty);
1968 return;
1969 }
Matt Spinlerde818812018-12-11 16:39:20 -06001970 else if (req.method() == "DELETE"_method)
1971 {
1972 handleDelete(req, res, objectPath);
1973 return;
1974 }
Ed Tanous049a0512018-11-01 13:58:42 -07001975
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001976 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1977 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001978 res.end();
1979}
1980
Ed Tanous1abe55e2018-09-05 08:30:59 -07001981template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1982{
1983 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03001984 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07001985 .methods("GET"_method)(
1986 [](const crow::Request &req, crow::Response &res) {
1987 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1988 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001989 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001990 });
1991
1992 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03001993 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07001994 .methods("GET"_method)(
1995 [](const crow::Request &req, crow::Response &res) {
1996 auto myCallback = [&res](const boost::system::error_code ec,
1997 std::vector<std::string> &names) {
1998 if (ec)
1999 {
2000 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2001 res.result(
2002 boost::beast::http::status::internal_server_error);
2003 }
2004 else
2005 {
2006 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002007 res.jsonValue = {{"status", "ok"}};
2008 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002009 for (auto &name : names)
2010 {
2011 objectsSub.push_back({{"name", name}});
2012 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002013 }
2014 res.end();
2015 };
2016 crow::connections::systemBus->async_method_call(
2017 std::move(myCallback), "org.freedesktop.DBus", "/",
2018 "org.freedesktop.DBus", "ListNames");
2019 });
2020
2021 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002022 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002023 .methods("GET"_method)(
2024 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002025 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002026 });
2027
2028 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002029 .requires({"Login"})
2030 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2031 const std::string &path) {
2032 std::string objectPath = "/xyz/" + path;
2033 handleDBusUrl(req, res, objectPath);
2034 });
2035
2036 BMCWEB_ROUTE(app, "/xyz/<path>")
2037 .requires({"ConfigureComponents", "ConfigureManager"})
2038 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002039 [](const crow::Request &req, crow::Response &res,
2040 const std::string &path) {
2041 std::string objectPath = "/xyz/" + path;
2042 handleDBusUrl(req, res, objectPath);
2043 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002044
Ed Tanous049a0512018-11-01 13:58:42 -07002045 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002046 .requires({"Login"})
2047 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2048 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002049 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002050 handleDBusUrl(req, res, objectPath);
2051 });
2052
2053 BMCWEB_ROUTE(app, "/org/<path>")
2054 .requires({"ConfigureComponents", "ConfigureManager"})
2055 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002056 [](const crow::Request &req, crow::Response &res,
2057 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002058 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002059 handleDBusUrl(req, res, objectPath);
2060 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002061
Ed Tanous1abe55e2018-09-05 08:30:59 -07002062 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002063 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002064 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2065 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002066 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002067 if (!std::regex_match(dumpId, validFilename))
2068 {
Ed Tanousad18f072018-11-14 14:07:48 -08002069 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002070 res.end();
2071 return;
2072 }
James Feistf6150402019-01-08 10:36:20 -08002073 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002074 "/var/lib/phosphor-debug-collector/dumps");
2075
Ed Tanousad18f072018-11-14 14:07:48 -08002076 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002077
James Feistf6150402019-01-08 10:36:20 -08002078 if (!std::filesystem::exists(loc) ||
2079 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002080 {
Ed Tanousad18f072018-11-14 14:07:48 -08002081 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002082 res.result(boost::beast::http::status::not_found);
2083 res.end();
2084 return;
2085 }
James Feistf6150402019-01-08 10:36:20 -08002086 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002087
Ed Tanous1abe55e2018-09-05 08:30:59 -07002088 for (auto &file : files)
2089 {
2090 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002091 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002092 {
2093 continue;
2094 }
2095 res.addHeader("Content-Type", "application/octet-stream");
2096 res.body() = {std::istreambuf_iterator<char>(readFile),
2097 std::istreambuf_iterator<char>()};
2098 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002099 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002100 }
2101 res.result(boost::beast::http::status::not_found);
2102 res.end();
2103 return;
2104 });
2105
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002106 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002107 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002108 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002109 const std::string &Connection) {
2110 introspectObjects(Connection, "/",
2111 std::make_shared<bmcweb::AsyncResp>(res));
2112 });
2113
2114 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2115 .methods("GET"_method,
2116 "POST"_method)([](const crow::Request &req,
2117 crow::Response &res,
2118 const std::string &processName,
2119 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002120 std::vector<std::string> strs;
2121 boost::split(strs, requestedPath, boost::is_any_of("/"));
2122 std::string objectPath;
2123 std::string interfaceName;
2124 std::string methodName;
2125 auto it = strs.begin();
2126 if (it == strs.end())
2127 {
2128 objectPath = "/";
2129 }
2130 while (it != strs.end())
2131 {
2132 // Check if segment contains ".". If it does, it must be an
2133 // interface
2134 if (it->find(".") != std::string::npos)
2135 {
2136 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002137 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07002138 // as part of our <path> specifier above, which causes the
2139 // normal trailing backslash redirector to fail.
2140 }
2141 else if (!it->empty())
2142 {
2143 objectPath += "/" + *it;
2144 }
2145 it++;
2146 }
2147 if (it != strs.end())
2148 {
2149 interfaceName = *it;
2150 it++;
2151
2152 // after interface, we might have a method name
2153 if (it != strs.end())
2154 {
2155 methodName = *it;
2156 it++;
2157 }
2158 }
2159 if (it != strs.end())
2160 {
2161 // if there is more levels past the method name, something went
2162 // wrong, return not found
2163 res.result(boost::beast::http::status::not_found);
2164 res.end();
2165 return;
2166 }
2167 if (interfaceName.empty())
2168 {
2169 crow::connections::systemBus->async_method_call(
2170 [&, processName,
2171 objectPath](const boost::system::error_code ec,
2172 const std::string &introspect_xml) {
2173 if (ec)
2174 {
2175 BMCWEB_LOG_ERROR
2176 << "Introspect call failed with error: "
2177 << ec.message()
2178 << " on process: " << processName
2179 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002180 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002181 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002182 tinyxml2::XMLDocument doc;
2183
2184 doc.Parse(introspect_xml.c_str());
2185 tinyxml2::XMLNode *pRoot =
2186 doc.FirstChildElement("node");
2187 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002188 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002189 BMCWEB_LOG_ERROR << "XML document failed to parse "
2190 << processName << " " << objectPath
2191 << "\n";
2192 res.jsonValue = {{"status", "XML parse error"}};
2193 res.result(boost::beast::http::status::
2194 internal_server_error);
2195 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002196 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002197
2198 BMCWEB_LOG_DEBUG << introspect_xml;
2199 res.jsonValue = {{"status", "ok"},
2200 {"bus_name", processName},
2201 {"object_path", objectPath}};
2202 nlohmann::json &interfacesArray =
2203 res.jsonValue["interfaces"];
2204 interfacesArray = nlohmann::json::array();
2205 tinyxml2::XMLElement *interface =
2206 pRoot->FirstChildElement("interface");
2207
2208 while (interface != nullptr)
2209 {
2210 const char *ifaceName =
2211 interface->Attribute("name");
2212 if (ifaceName != nullptr)
2213 {
2214 interfacesArray.push_back(
2215 {{"name", ifaceName}});
2216 }
2217
2218 interface =
2219 interface->NextSiblingElement("interface");
2220 }
2221
Ed Tanous1abe55e2018-09-05 08:30:59 -07002222 res.end();
2223 },
2224 processName, objectPath,
2225 "org.freedesktop.DBus.Introspectable", "Introspect");
2226 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002227 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002228 {
2229 crow::connections::systemBus->async_method_call(
2230 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002231 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07002232 const boost::system::error_code ec,
2233 const std::string &introspect_xml) {
2234 if (ec)
2235 {
2236 BMCWEB_LOG_ERROR
2237 << "Introspect call failed with error: "
2238 << ec.message()
2239 << " on process: " << processName
2240 << " path: " << objectPath << "\n";
2241 }
2242 else
2243 {
2244 tinyxml2::XMLDocument doc;
2245
2246 doc.Parse(introspect_xml.c_str());
2247 tinyxml2::XMLNode *pRoot =
2248 doc.FirstChildElement("node");
2249 if (pRoot == nullptr)
2250 {
2251 BMCWEB_LOG_ERROR
2252 << "XML document failed to parse "
2253 << processName << " " << objectPath << "\n";
2254 res.result(boost::beast::http::status::
2255 internal_server_error);
2256 }
2257 else
2258 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002259 // if we know we're the only call, build the
2260 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07002261 tinyxml2::XMLElement *interface =
2262 pRoot->FirstChildElement("interface");
2263
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002264 res.jsonValue = {
2265 {"status", "ok"},
2266 {"bus_name", processName},
2267 {"interface", interfaceName},
2268 {"object_path", objectPath},
2269 {"properties", nlohmann::json::object()}};
2270
2271 nlohmann::json &methodsArray =
2272 res.jsonValue["methods"];
2273 methodsArray = nlohmann::json::array();
2274
2275 nlohmann::json &signalsArray =
2276 res.jsonValue["signals"];
2277 signalsArray = nlohmann::json::array();
2278
Ed Tanous1abe55e2018-09-05 08:30:59 -07002279 while (interface != nullptr)
2280 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002281 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002282 interface->Attribute("name");
2283
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002284 if (ifaceName != nullptr &&
2285 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002286 {
2287 tinyxml2::XMLElement *methods =
2288 interface->FirstChildElement(
2289 "method");
2290 while (methods != nullptr)
2291 {
2292 nlohmann::json argsArray =
2293 nlohmann::json::array();
2294 tinyxml2::XMLElement *arg =
2295 methods->FirstChildElement(
2296 "arg");
2297 while (arg != nullptr)
2298 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002299 nlohmann::json thisArg;
2300 for (const char *fieldName :
2301 std::array<const char *,
2302 3>{"name",
2303 "direction",
2304 "type"})
2305 {
2306 const char *fieldValue =
2307 arg->Attribute(
2308 fieldName);
2309 if (fieldValue != nullptr)
2310 {
2311 thisArg[fieldName] =
2312 fieldValue;
2313 }
2314 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002315 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002316 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07002317 arg = arg->NextSiblingElement(
2318 "arg");
2319 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002320
2321 const char *name =
2322 methods->Attribute("name");
2323 if (name != nullptr)
2324 {
2325 methodsArray.push_back(
2326 {{"name", name},
2327 {"uri", "/bus/system/" +
2328 processName +
2329 objectPath +
2330 "/" +
2331 interfaceName +
2332 "/" + name},
2333 {"args", argsArray}});
2334 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002335 methods =
2336 methods->NextSiblingElement(
2337 "method");
2338 }
2339 tinyxml2::XMLElement *signals =
2340 interface->FirstChildElement(
2341 "signal");
2342 while (signals != nullptr)
2343 {
2344 nlohmann::json argsArray =
2345 nlohmann::json::array();
2346
2347 tinyxml2::XMLElement *arg =
2348 signals->FirstChildElement(
2349 "arg");
2350 while (arg != nullptr)
2351 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002352 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002353 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002354 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002355 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002356 if (name != nullptr &&
2357 type != nullptr)
2358 {
2359 argsArray.push_back({
2360 {"name", name},
2361 {"type", type},
2362 });
2363 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002364 arg = arg->NextSiblingElement(
2365 "arg");
2366 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002367 const char *name =
2368 signals->Attribute("name");
2369 if (name != nullptr)
2370 {
2371 signalsArray.push_back(
2372 {{"name", name},
2373 {"args", argsArray}});
2374 }
2375
Ed Tanous1abe55e2018-09-05 08:30:59 -07002376 signals =
2377 signals->NextSiblingElement(
2378 "signal");
2379 }
2380
Ed Tanous1abe55e2018-09-05 08:30:59 -07002381 break;
2382 }
2383
2384 interface = interface->NextSiblingElement(
2385 "interface");
2386 }
2387 if (interface == nullptr)
2388 {
2389 // if we got to the end of the list and
2390 // never found a match, throw 404
2391 res.result(
2392 boost::beast::http::status::not_found);
2393 }
2394 }
2395 }
2396 res.end();
2397 },
2398 processName, objectPath,
2399 "org.freedesktop.DBus.Introspectable", "Introspect");
2400 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002401 else
2402 {
2403 if (req.method() != "POST"_method)
2404 {
2405 res.result(boost::beast::http::status::not_found);
2406 res.end();
2407 return;
2408 }
2409
2410 nlohmann::json requestDbusData =
2411 nlohmann::json::parse(req.body, nullptr, false);
2412
2413 if (requestDbusData.is_discarded())
2414 {
2415 res.result(boost::beast::http::status::bad_request);
2416 res.end();
2417 return;
2418 }
2419 if (!requestDbusData.is_array())
2420 {
2421 res.result(boost::beast::http::status::bad_request);
2422 res.end();
2423 return;
2424 }
2425 auto transaction = std::make_shared<InProgressActionData>(res);
2426
2427 transaction->path = objectPath;
2428 transaction->methodName = methodName;
2429 transaction->arguments = std::move(requestDbusData);
2430
2431 findActionOnInterface(transaction, processName);
2432 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002433 });
2434}
2435} // namespace openbmc_mapper
2436} // namespace crow