blob: 217345845b78601ec07fc0d89b75265e522956e1 [file] [log] [blame]
#include "software_manager.hpp"
#include <boost/container/flat_map.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/async.hpp>
#include <sdbusplus/async/context.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
#include <xyz/openbmc_project/Association/Definitions/server.hpp>
#include <xyz/openbmc_project/ObjectMapper/client.hpp>
#include <xyz/openbmc_project/Software/Version/client.hpp>
#include <xyz/openbmc_project/State/Host/client.hpp>
#include <cstdint>
PHOSPHOR_LOG2_USING;
using namespace phosphor::software::manager;
using AsyncMatch = sdbusplus::async::match;
namespace RulesIntf = sdbusplus::bus::match::rules;
static constexpr auto serviceNameEM = "xyz.openbmc_project.EntityManager";
const auto matchRuleSender = RulesIntf::sender(serviceNameEM);
const auto matchRulePath = RulesIntf::path("/xyz/openbmc_project/inventory");
SoftwareManager::SoftwareManager(sdbusplus::async::context& ctx,
const std::string& serviceNameSuffix) :
ctx(ctx),
configIntfAddedMatch(ctx, RulesIntf::interfacesAdded() + matchRuleSender),
configIntfRemovedMatch(ctx, RulesIntf::interfacesRemoved() + matchRulePath),
serviceNameSuffix(serviceNameSuffix),
manager(ctx, sdbusplus::client::xyz::openbmc_project::software::Version<>::
namespace_path)
{
const std::string serviceNameFull =
"xyz.openbmc_project.Software." + serviceNameSuffix;
debug("requesting dbus name {BUSNAME}", "BUSNAME", serviceNameFull);
ctx.request_name(serviceNameFull.c_str());
debug("Initialized SoftwareManager");
}
// NOLINTBEGIN(readability-static-accessed-through-instance)
static sdbusplus::async::task<std::optional<SoftwareConfig>> getConfig(
sdbusplus::async::context& ctx, const std::string& service,
const std::string& objectPath, const std::string& interfacePrefix)
// NOLINTEND(readability-static-accessed-through-instance)
{
auto client = sdbusplus::async::proxy()
.service(service)
.path(objectPath)
.interface("org.freedesktop.DBus.Properties");
uint64_t vendorIANA = 0;
std::string compatible{};
std::string configType{};
std::string configName{};
const std::string interfaceName = interfacePrefix + ".FirmwareInfo";
try
{
{
auto propVendorIANA = co_await client.call<std::variant<uint64_t>>(
ctx, "Get", interfaceName, "VendorIANA");
vendorIANA = std::get<uint64_t>(propVendorIANA);
}
{
auto propCompatible =
co_await client.call<std::variant<std::string>>(
ctx, "Get", interfaceName, "CompatibleHardware");
compatible = std::get<std::string>(propCompatible);
}
{
auto propConfigType =
co_await client.call<std::variant<std::string>>(
ctx, "Get", interfacePrefix, "Type");
configType = std::get<std::string>(propConfigType);
}
{
auto propConfigName =
co_await client.call<std::variant<std::string>>(
ctx, "Get", interfacePrefix, "Name");
configName = std::get<std::string>(propConfigName);
}
}
catch (std::exception& e)
{
error("Failed to get config with {ERROR}", "ERROR", e);
co_return std::nullopt;
}
co_return SoftwareConfig(objectPath, vendorIANA, compatible, configType,
configName);
}
// NOLINTBEGIN(readability-static-accessed-through-instance)
sdbusplus::async::task<> SoftwareManager::initDevices(
const std::vector<std::string>& configurationInterfaces)
// NOLINTEND(readability-static-accessed-through-instance)
{
ctx.spawn(interfaceAddedMatch(configurationInterfaces));
ctx.spawn(interfaceRemovedMatch(configurationInterfaces));
auto client = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>(ctx)
.service("xyz.openbmc_project.ObjectMapper")
.path("/xyz/openbmc_project/object_mapper");
auto res = co_await client.get_sub_tree("/xyz/openbmc_project/inventory", 0,
configurationInterfaces);
for (auto& iface : configurationInterfaces)
{
debug("[config] looking for dbus interface {INTF}", "INTF", iface);
}
for (auto& [path, v] : res)
{
for (auto& [service, interfaceNames] : v)
{
std::string interfaceFound;
for (std::string& interfaceName : interfaceNames)
{
for (auto& iface : configurationInterfaces)
{
if (interfaceName == iface)
{
interfaceFound = interfaceName;
}
}
}
if (interfaceFound.empty())
{
continue;
}
co_await handleInterfaceAdded(service, path, interfaceFound);
}
}
debug("Done with initial configuration");
}
// NOLINTBEGIN(readability-static-accessed-through-instance)
sdbusplus::async::task<void> SoftwareManager::handleInterfaceAdded(
const std::string& service, const std::string& path,
const std::string& interface)
// NOLINTEND(readability-static-accessed-through-instance)
{
debug("Found configuration interface at {SERVICE}, {PATH}", "SERVICE",
service, "PATH", path);
auto optConfig = co_await getConfig(ctx, service, path, interface);
if (!optConfig.has_value())
{
error("Failed to get configuration from {PATH}", "PATH", path);
co_return;
}
if (devices.contains(optConfig.value().objectPath))
{
error("Device configured from {PATH} is already known", "PATH",
optConfig.value().objectPath);
co_return;
}
co_await initDevice(service, path, optConfig.value());
co_return;
}
using BasicVariantType =
std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
using InterfacesMap = boost::container::flat_map<std::string, BasicVariantType>;
using ConfigMap = boost::container::flat_map<std::string, InterfacesMap>;
// NOLINTBEGIN(readability-static-accessed-through-instance)
sdbusplus::async::task<void> SoftwareManager::interfaceAddedMatch(
std::vector<std::string> interfaces)
// NOLINTEND(readability-static-accessed-through-instance)
{
while (!ctx.stop_requested())
{
std::tuple<std::string, ConfigMap> nextResult("", {});
nextResult = co_await configIntfAddedMatch
.next<sdbusplus::message::object_path, ConfigMap>();
auto& [objPath, interfacesMap] = nextResult;
for (auto& interface : interfaces)
{
if (interfacesMap.contains(interface))
{
debug("detected interface {INTF} added on {PATH}", "INTF",
interface, "PATH", objPath);
co_await handleInterfaceAdded(serviceNameEM, objPath,
interface);
}
}
}
}
// NOLINTBEGIN(readability-static-accessed-through-instance)
sdbusplus::async::task<void> SoftwareManager::interfaceRemovedMatch(
std::vector<std::string> interfaces)
// NOLINTEND(readability-static-accessed-through-instance)
{
while (!ctx.stop_requested())
{
auto nextResult = co_await configIntfRemovedMatch.next<
sdbusplus::message::object_path, std::vector<std::string>>();
auto& [objPath, interfacesRemoved] = nextResult;
debug("detected interface removed on {PATH}", "PATH", objPath);
for (auto& interface : interfaces)
{
if (std::ranges::find(interfacesRemoved, interface) !=
interfacesRemoved.end())
{
debug("detected interface {INTF} removed on {PATH}", "INTF",
interface, "PATH", objPath);
co_await handleInterfaceRemoved(objPath);
}
}
}
}
sdbusplus::async::task<void> SoftwareManager::handleInterfaceRemoved(
const sdbusplus::message::object_path& objPath)
{
if (!devices.contains(objPath))
{
debug("could not find a device to remove");
co_return;
}
if (devices[objPath]->updateInProgress)
{
// TODO: This code path needs to be cleaned up in the future to
// eventually remove the device.
debug(
"removal of device at {PATH} ignored because of in-progress update",
"PATH", objPath.str);
co_return;
}
debug("removing device at {PATH}", "PATH", objPath.str);
devices.erase(objPath);
}