blob: 485d6b605061b94c63f4911498b48026892d78cb [file] [log] [blame]
#include "guid.hpp"
#include <ipmid/api.h>
#include <ipmid/utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/lg2.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <sstream>
#include <string>
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
static std::optional<command::Guid> guid;
namespace command
{
std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr);
static constexpr auto propInterface = "xyz.openbmc_project.Common.UUID";
static constexpr auto uuidProperty = "UUID";
static constexpr auto subtreePath = "/xyz/openbmc_project/inventory";
static void rfcToGuid(std::string rfc4122, Guid& uuid)
{
using Argument = xyz::openbmc_project::Common::InvalidArgument;
// UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
// Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
// order
// Ex: 0x2332fc2c40e66298e511f2782395a361
constexpr size_t uuidHexLength = (2 * BMC_GUID_LEN);
constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
if (rfc4122.size() == uuidRfc4122Length)
{
rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
rfc4122.end());
}
if (rfc4122.size() != uuidHexLength)
{
elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
Argument::ARGUMENT_VALUE(rfc4122.c_str()));
}
for (size_t ind = 0; ind < uuidHexLength; ind += 2)
{
long b;
try
{
b = std::stoul(rfc4122.substr(ind, 2), nullptr, 16);
}
catch (const std::exception& e)
{
elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
Argument::ARGUMENT_VALUE(rfc4122.c_str()));
}
uuid[BMC_GUID_LEN - (ind / 2) - 1] = static_cast<uint8_t>(b);
}
return;
}
// Canned System GUID for when the Chassis DBUS object is not populated
static constexpr Guid fakeGuid = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10};
const Guid& getSystemGUID()
{
if (guid.has_value())
{
return guid.value();
}
sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
ipmi::Value propValue;
try
{
const auto& [objPath, service] =
ipmi::getDbusObject(bus, propInterface, subtreePath);
// Read UUID property value from bmcObject
// UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
propValue = ipmi::getDbusProperty(bus, service, objPath, propInterface,
uuidProperty);
}
catch (const sdbusplus::exception_t& e)
{
lg2::error("Failed in reading BMC UUID property: {ERROR}", "ERROR", e);
return fakeGuid;
}
std::string rfc4122Uuid = std::get<std::string>(propValue);
try
{
// convert to IPMI format
Guid tmpGuid{};
rfcToGuid(rfc4122Uuid, tmpGuid);
guid = tmpGuid;
}
catch (const InvalidArgument& e)
{
lg2::error("Failed in parsing BMC UUID property: {VALUE}", "VALUE",
rfc4122Uuid.c_str());
return fakeGuid;
}
return guid.value();
}
void registerGUIDChangeCallback()
{
if (matchPtr == nullptr)
{
using namespace sdbusplus::bus::match::rules;
sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
try
{
matchPtr = std::make_unique<sdbusplus::bus::match_t>(
bus, propertiesChangedNamespace(subtreePath, propInterface),
[](sdbusplus::message_t& m) {
try
{
std::string iface{};
std::map<std::string, ipmi::Value> pdict{};
m.read(iface, pdict);
if (iface != propInterface)
{
return;
}
auto guidStr = std::get<std::string>(pdict.at("UUID"));
Guid tmpGuid{};
rfcToGuid(guidStr, tmpGuid);
guid = tmpGuid;
}
catch (const std::exception& e)
{
// signal contained invalid guid; ignore it
lg2::error(
"Failed to parse propertiesChanged signal: {ERROR}",
"ERROR", e);
}
});
}
catch (const std::exception& e)
{
lg2::error("Failed to create dbus match: {ERROR}", "ERROR", e);
}
}
}
} // namespace command