Move bmcweb over to sdbusplus
This patchset moves bmcweb from using boost-dbus over entirely to
sdbusplus. This has some nice improvements in performance (about 30%
of CPU cycles saved in dbus transactions), as well as makes this
project manuver closer to the upstream way of thinking.
Changes to bmcweb are largely ceremonial, and fall into a few
categories:
1. Moves async_method_call instances to the new format, and deletes any
use of the "endpoint" object in leiu of the sdbusplus style interface
2. sdbus object_path object doesn't allow access to the string
directly, so code that uses it moves to explicit casts.
3. The mapbox variant, while attempting to recreate boost::variant,
misses a T* get<T*>() method implementation, which allows using variant
without exceptions. Currently, there is an overload for
mapbox::get_ptr implementation which replecates the functionality.
Tested by: Booting the bmcweb on a target, iterating through redfish
basic phosphor-webui usage, and websockets usage
Change-Id: I2d95882908d6eb6dba00b9219a221dd96449ca7b
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp
index 0306183..ab7ebef 100644
--- a/include/dbus_monitor.hpp
+++ b/include/dbus_monitor.hpp
@@ -1,7 +1,6 @@
#pragma once
-#include <dbus/filter.hpp>
-#include <dbus/match.hpp>
#include <dbus_singleton.hpp>
+#include <sdbusplus/bus/match.hpp>
#include <crow/app.h>
#include <boost/container/flat_map.hpp>
@@ -9,34 +8,38 @@
namespace dbus_monitor {
struct DbusWebsocketSession {
- std::vector<std::unique_ptr<dbus::match>> matches;
- std::vector<dbus::filter> filters;
+ std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
};
static boost::container::flat_map<crow::websocket::connection*,
DbusWebsocketSession>
sessions;
-void on_property_update(dbus::filter& filter, boost::system::error_code ec,
- dbus::message s) {
- if (!ec) {
- std::string object_name;
- std::vector<std::pair<std::string, dbus::dbus_variant>> values;
- s.unpack(object_name, values);
- nlohmann::json j;
- for (auto& value : values) {
- boost::apply_visitor([&](auto val) { j[s.get_path()] = val; },
- value.second);
- }
- auto data_to_send = j.dump();
-
- for (auto& session : sessions) {
- session.first->send_text(data_to_send);
- }
+int on_property_update(sd_bus_message* m, void* userdata,
+ sd_bus_error* ret_error) {
+ if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) {
+ CROW_LOG_ERROR << "Sdbus error in on_property_update";
+ return 0;
}
- filter.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
- on_property_update(filter, ec, s);
- });
+ sdbusplus::message::message message(m);
+ std::string object_name;
+ std::vector<
+ std::pair<std::string, sdbusplus::message::variant<
+ std::string, bool, int64_t, uint64_t, double>>>
+ values;
+ message.read(object_name, values);
+ nlohmann::json j;
+ const std::string& path = message.get_path();
+ for (auto& value : values) {
+ mapbox::util::apply_visitor([&](auto&& val) { j[path] = val; },
+ value.second);
+ }
+ std::string data_to_send = j.dump();
+
+ for (const std::pair<crow::websocket::connection*, DbusWebsocketSession>&
+ session : sessions) {
+ session.first->send_text(data_to_send);
+ }
};
template <typename... Middlewares>
@@ -56,19 +59,10 @@
"interface='org.freedesktop.DBus.Properties',"
"path_namespace='" +
path_namespace + "'");
- sessions[&conn].matches.push_back(std::make_unique<dbus::match>(
- crow::connections::system_bus, std::move(match_string)));
-
- sessions[&conn].filters.emplace_back(
- crow::connections::system_bus, [path_namespace](dbus::message m) {
- return m.get_member() == "PropertiesChanged" &&
- boost::starts_with(m.get_path(), path_namespace);
- });
- auto& this_filter = sessions[&conn].filters.back();
- this_filter.async_dispatch(
- [&](boost::system::error_code ec, dbus::message s) {
- on_property_update(this_filter, ec, s);
- });
+ sessions[&conn].matches.emplace_back(
+ std::make_unique<sdbusplus::bus::match::match>(
+ *crow::connections::system_bus, match_string,
+ on_property_update));
})
.onclose([&](crow::websocket::connection& conn,
@@ -78,5 +72,5 @@
CROW_LOG_ERROR << "Got unexpected message from client on sensorws";
});
}
-} // namespace redfish
+} // namespace dbus_monitor
} // namespace crow
diff --git a/include/dbus_singleton.hpp b/include/dbus_singleton.hpp
index e2fd2d6..e6be81b 100644
--- a/include/dbus_singleton.hpp
+++ b/include/dbus_singleton.hpp
@@ -1,10 +1,21 @@
#pragma once
-#include <dbus/connection.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <iostream>
+
+namespace mapbox {
+template <typename T, typename... Types>
+const T* get_ptr(const mapbox::util::variant<Types...>& v) {
+ if (v.template is<std::remove_const_t<T>>()) {
+ return &v.template get_unchecked<std::remove_const_t<T>>();
+ } else {
+ return nullptr;
+ }
+}
+} // namespace mapbox
namespace crow {
namespace connections {
+static std::shared_ptr<sdbusplus::asio::connection> system_bus;
-static std::shared_ptr<dbus::connection> system_bus;
-
-} // namespace dbus
-} // namespace crow
\ No newline at end of file
+} // namespace connections
+} // namespace crow
diff --git a/include/intel_oem.hpp b/include/intel_oem.hpp
index e821807..f2dc163 100644
--- a/include/intel_oem.hpp
+++ b/include/intel_oem.hpp
@@ -10,24 +10,29 @@
template <typename... Middlewares>
void request_routes(Crow<Middlewares...>& app) {
CROW_ROUTE(app, "/intel/firmwareupload")
- .methods("POST"_method)([](const crow::request& req) {
+ .methods(
+ "POST"_method)([](const crow::request& req, crow::response& res) {
std::string filepath("/tmp/fw_update_image");
std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
std::ofstream::trunc);
out << req.body;
out.close();
+ crow::connections::system_bus->async_method_call(
+ [&](boost::system::error_code ec) {
+ std::cout << "async_method_call callback\n";
+ if (ec) {
+ std::cerr << "error with async_method_call \n";
+ res.json_value["status"] = "Upload failed";
+ } else {
+ res.json_value["status"] = "Upload Successful";
+ }
+ res.end();
+ },
+ "xyz.openbmc_project.fwupdate1.server",
+ "/xyz/openbmc_project/fwupdate1", "xyz.openbmc_project.fwupdate1",
+ "start", "file://" + filepath);
- auto m = dbus::message::new_call(
- {"xyz.openbmc_project.fwupdate1.server",
- "/xyz/openbmc_project/fwupdate1", "xyz.openbmc_project.fwupdate1"},
- "start");
-
- m.pack(std::string("file://") + filepath);
- crow::connections::system_bus->send(m);
- nlohmann::json j;
- j["status"] = "Upload Successful";
- return j;
});
}
-} // namespace redfish
+} // namespace intel_oem
} // namespace crow
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index d42c04b..4257127 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -1,11 +1,6 @@
#include <crow/app.h>
#include <tinyxml2.h>
-#include <dbus/connection.hpp>
-#include <dbus/endpoint.hpp>
-#include <dbus/filter.hpp>
-#include <dbus/match.hpp>
-#include <dbus/message.hpp>
#include <dbus_singleton.hpp>
#include <boost/container/flat_set.hpp>
@@ -15,12 +10,11 @@
void introspect_objects(crow::response &res, std::string process_name,
std::string path,
std::shared_ptr<nlohmann::json> transaction) {
- dbus::endpoint introspect_endpoint(
- process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
crow::connections::system_bus->async_method_call(
- [&, process_name{std::move(process_name)}, object_path{std::move(path)} ](
- const boost::system::error_code ec,
- const std::string &introspect_xml) {
+ [
+ &res, transaction, process_name{std::move(process_name)},
+ object_path{std::move(path)}
+ ](const boost::system::error_code ec, const std::string &introspect_xml) {
if (ec) {
CROW_LOG_ERROR << "Introspect call failed with error: "
<< ec.message() << " on process: " << process_name
@@ -35,7 +29,7 @@
tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
if (pRoot == nullptr) {
CROW_LOG_ERROR << "XML document failed to parse " << process_name
- << " " << path << "\n";
+ << " " << object_path << "\n";
} else {
tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
@@ -61,14 +55,23 @@
res.end();
}
},
- introspect_endpoint);
+ process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
}
-using ManagedObjectType = std::vector<std::pair<
- dbus::object_path, boost::container::flat_map<
- std::string, boost::container::flat_map<
- std::string, dbus::dbus_variant>>>>;
-void get_manged_objects_for_enumerate(
+// A smattering of common types to unpack. TODO(ed) this should really iterate
+// the sdbusplus object directly and build the json response
+using DbusRestVariantType = sdbusplus::message::variant<
+ std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
+ int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
+ bool>;
+
+using ManagedObjectType = std::vector<std::pair<
+ sdbusplus::message::object_path,
+ boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<std::string, DbusRestVariantType>>>>;
+
+void get_managed_objects_for_enumerate(
const std::string &object_name, const std::string &connection_name,
crow::response &res, std::shared_ptr<nlohmann::json> transaction) {
crow::connections::system_bus->async_method_call(
@@ -78,13 +81,29 @@
CROW_LOG_ERROR << ec;
} else {
nlohmann::json &data_json = *transaction;
+
for (auto &object_path : objects) {
- nlohmann::json &object_json = data_json[object_path.first.value];
+ CROW_LOG_DEBUG << "Reading object "
+ << static_cast<const std::string&>(object_path.first);
+ nlohmann::json &object_json =
+ data_json[static_cast<const std::string&>(object_path.first)];
+ if (object_json.is_null()) {
+ object_json = nlohmann::json::object();
+ }
for (const auto &interface : object_path.second) {
for (const auto &property : interface.second) {
- boost::apply_visitor(
- [&](auto &&val) { object_json[property.first] = val; },
+ nlohmann::json &property_json = object_json[property.first];
+ mapbox::util::apply_visitor(
+ [&property_json](auto &&val) { property_json = val; },
property.second);
+
+ // dbus-rest represents booleans as 1 or 0, implement to match
+ // TODO(ed) see if dbus-rest should be changed
+ const bool *property_bool =
+ property_json.get_ptr<const bool *>();
+ if (property_bool != nullptr) {
+ property_json = *property_bool ? 1 : 0;
+ }
}
}
}
@@ -97,9 +116,9 @@
res.end();
}
},
- {connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
- "GetManagedObjects"});
-} // namespace openbmc_mapper
+ connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+}
using GetSubTreeType = std::vector<
std::pair<std::string,
@@ -132,14 +151,14 @@
auto transaction =
std::make_shared<nlohmann::json>(nlohmann::json::object());
for (const std::string &connection : connections) {
- get_manged_objects_for_enumerate(object_path, connection, res,
- transaction);
+ get_managed_objects_for_enumerate(object_path, connection, res,
+ transaction);
}
},
- {"xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper",
- "xyz.openbmc_project.ObjectMapper", "GetSubTree"},
- object_path, (int32_t)0, std::array<std::string, 0>());
+ "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree", object_path, (int32_t)0,
+ std::array<std::string, 0>());
}
template <typename... Middlewares>
@@ -151,33 +170,32 @@
CROW_ROUTE(app, "/bus/system/")
.methods("GET"_method)([](const crow::request &req, crow::response &res) {
+
+ auto myCallback = [&res](const boost::system::error_code ec,
+ std::vector<std::string> &names) {
+ if (ec) {
+ res.code = 500;
+ } else {
+ std::sort(names.begin(), names.end());
+ nlohmann::json j{{"status", "ok"}};
+ auto &objects_sub = j["objects"];
+ for (auto &name : names) {
+ objects_sub.push_back({{"name", name}});
+ }
+ res.json_value = std::move(j);
+ }
+ res.end();
+ };
crow::connections::system_bus->async_method_call(
- [&](const boost::system::error_code ec,
- std::vector<std::string> &names) {
-
- if (ec) {
- res.code = 500;
- } else {
- std::sort(names.begin(), names.end());
- nlohmann::json j{{"status", "ok"}};
- auto &objects_sub = j["objects"];
- for (auto &name : names) {
- objects_sub.push_back({{"name", name}});
- }
- res.json_value = std::move(j);
- }
- res.end();
- },
- {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"});
-
+ std::move(myCallback), "org.freedesktop.DBus", "/",
+ "org.freedesktop.DBus", "ListNames");
});
CROW_ROUTE(app, "/list/")
.methods("GET"_method)([](const crow::request &req, crow::response &res) {
crow::connections::system_bus->async_method_call(
- [&](const boost::system::error_code ec,
- const std::vector<std::string> &object_paths) {
-
+ [&res](const boost::system::error_code ec,
+ const std::vector<std::string> &object_paths) {
if (ec) {
res.code = 500;
} else {
@@ -187,10 +205,10 @@
}
res.end();
},
- {"xyz.openbmc_project.ObjectMapper",
- "/xyz/openbmc_project/object_mapper",
- "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths"},
- "", static_cast<int32_t>(99), std::array<std::string, 0>());
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "",
+ static_cast<int32_t>(99), std::array<std::string, 0>());
});
CROW_ROUTE(app, "/xyz/<path>")
@@ -242,9 +260,9 @@
crow::connections::system_bus->async_method_call(
[
- &, object_path{std::move(object_path)},
- dest_property{std::move(dest_property)},
- property_set_value{std::move(property_set_value)}, transaction
+ &res, &req, object_path{std::move(object_path)},
+ dest_property{std::move(dest_property)},
+ property_set_value{std::move(property_set_value)}, transaction
](const boost::system::error_code ec,
const GetObjectType &object_names) {
if (ec) {
@@ -260,17 +278,17 @@
if (req.method == "GET"_method) {
for (auto &interface : object_names[0].second) {
crow::connections::system_bus->async_method_call(
- [&](const boost::system::error_code ec,
+ [&res, transaction](
+ const boost::system::error_code ec,
const std::vector<std::pair<
- std::string, dbus::dbus_variant>> &properties) {
+ std::string, DbusRestVariantType>> &properties) {
if (ec) {
CROW_LOG_ERROR << "Bad dbus request error: " << ec;
} else {
for (auto &property : properties) {
- boost::apply_visitor(
- [&](auto val) {
- (*transaction)[property.first] = val;
- },
+ nlohmann::json &it = (*transaction)[property.first];
+ mapbox::util::apply_visitor(
+ [&it](auto &&val) { it = val; },
property.second);
}
}
@@ -282,9 +300,8 @@
res.end();
}
},
- {object_names[0].first, object_path,
- "org.freedesktop.DBus.Properties", "GetAll"},
- interface);
+ object_names[0].first, object_path,
+ "org.freedesktop.DBus.Properties", "GetAll", interface);
}
} else if (req.method == "PUT"_method) {
for (auto &interface : object_names[0].second) {
@@ -298,18 +315,19 @@
transaction
](const boost::system::error_code ec,
const boost::container::flat_map<
- std::string, dbus::dbus_variant> &properties) {
+ std::string, DbusRestVariantType> &properties) {
if (ec) {
CROW_LOG_ERROR << "Bad dbus request error: " << ec;
} else {
auto it = properties.find(dest_property);
if (it != properties.end()) {
// find the matched property in the interface
- dbus::dbus_variant property_value(
- property_set_value); // create the dbus
- // variant for dbus call
+ DbusRestVariantType property_value(
+ property_set_value);
+ // create the dbus variant for dbus call
crow::connections::system_bus->async_method_call(
- [&](const boost::system::error_code ec) {
+ [transaction](
+ const boost::system::error_code ec) {
// use the method "Set" to set the property
// value
if (ec) {
@@ -323,8 +341,8 @@
{"data", nullptr}};
},
- {object_names[0].first, object_path,
- "org.freedesktop.DBus.Properties", "Set"},
+ object_names[0].first, object_path,
+ "org.freedesktop.DBus.Properties", "Set",
interface, dest_property, property_value);
}
}
@@ -350,16 +368,15 @@
return;
}
},
- {object_names[0].first, object_path,
- "org.freedesktop.DBus.Properties", "GetAll"},
- interface);
+ object_names[0].first, object_path,
+ "org.freedesktop.DBus.Properties", "GetAll", interface);
}
}
},
- {"xyz.openbmc_project.ObjectMapper",
- "/xyz/openbmc_project/object_mapper",
- "xyz.openbmc_project.ObjectMapper", "GetObject"},
- object_path, std::array<std::string, 0>());
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject", object_path,
+ std::array<std::string, 0>());
});
CROW_ROUTE(app, "/bus/system/<str>/")
@@ -413,9 +430,6 @@
res.end();
return;
}
- dbus::endpoint introspect_endpoint(
- process_name, object_path, "org.freedesktop.DBus.Introspectable",
- "Introspect");
if (interface_name.empty()) {
crow::connections::system_bus->async_method_call(
[
@@ -459,7 +473,8 @@
}
res.end();
},
- introspect_endpoint);
+ process_name, object_path, "org.freedesktop.DBus.Introspectable",
+ "Introspect");
} else {
crow::connections::system_bus->async_method_call(
[
@@ -566,10 +581,11 @@
}
res.end();
},
- introspect_endpoint);
+ process_name, object_path, "org.freedesktop.DBus.Introspectable",
+ "Introspect");
}
});
-}
+} // namespace openbmc_mapper
} // namespace openbmc_mapper
} // namespace crow
diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp
index c28208d..03b9051 100644
--- a/include/redfish_v1.hpp
+++ b/include/redfish_v1.hpp
@@ -1,10 +1,6 @@
#pragma once
-#include <dbus/connection.hpp>
-#include <dbus/endpoint.hpp>
-#include <dbus/filter.hpp>
-#include <dbus/match.hpp>
-#include <dbus/message.hpp>
+#include <dbus_singleton.hpp>
#include <persistent_data_middleware.hpp>
#include <token_authorization_middleware.hpp>
#include <fstream>
@@ -43,10 +39,13 @@
return result;
}
+// GetManagedObjects unpack type. Observe that variant has only one bool type,
+// because we don't actually use the values it provides
using ManagedObjectType = std::vector<std::pair<
- dbus::object_path, boost::container::flat_map<
- std::string, boost::container::flat_map<
- std::string, dbus::dbus_variant>>>>;
+ sdbusplus::message::object_path,
+ boost::container::flat_map<
+ std::string, boost::container::flat_map<
+ std::string, sdbusplus::message::variant<bool>>>>>;
template <typename... Middlewares>
void request_routes(Crow<Middlewares...>& app) {
@@ -79,7 +78,8 @@
nlohmann::json member_array = nlohmann::json::array();
int user_index = 0;
for (auto& user : users) {
- const std::string& path = user.first.value;
+ const std::string& path =
+ static_cast<std::string>(user.first);
std::size_t last_index = path.rfind("/");
if (last_index == std::string::npos) {
last_index = 0;
@@ -94,8 +94,8 @@
}
res.end();
},
- {"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
- "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
+ "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
});
CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
@@ -110,7 +110,8 @@
res.code = 500;
} else {
for (auto& user : users) {
- const std::string& path = user.first.value;
+ const std::string& path =
+ static_cast<std::string>(user.first);
std::size_t last_index = path.rfind("/");
if (last_index == std::string::npos) {
last_index = 0;
@@ -145,8 +146,8 @@
}
res.end();
},
- {"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
- "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
+ "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
+ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
});
}
} // namespace redfish
diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp
index 59e9cca..07540d4 100644
--- a/include/token_authorization_middleware.hpp
+++ b/include/token_authorization_middleware.hpp
@@ -173,7 +173,8 @@
bool is_on_whitelist(const crow::request& req) const {
// it's allowed to GET root node without authentication
if ("GET"_method == req.method) {
- if (req.url == "/redfish/v1") {
+ CROW_LOG_DEBUG << "TESTING ROUTE " << req.url;
+ if (req.url == "/redfish/v1" || req.url == "/redfish/v1/") {
return true;
} else if (crow::webassets::routes.find(req.url) !=
crow::webassets::routes.end()) {
@@ -183,7 +184,8 @@
// it's allowed to POST on session collection & login without authentication
if ("POST"_method == req.method) {
- if ((req.url == "/redfish/v1/SessionService/Sessions") ||
+ if ((req.url == "/redfish/v1/SessionService/Sessions" ||
+ req.url == "/redfish/v1/SessionService/Sessions/") ||
(req.url == "/login") || (req.url == "/logout")) {
return true;
}