blob: a6405caa830aef9bc0188a74df3f8333ed5ae9e7 [file] [log] [blame] [edit]
#include "psm_handler.hpp"
#include "log.hpp"
#include <fmt/printf.h>
#include <variant>
namespace boot_time_monitor
{
namespace psm
{
namespace btm = boot_time_monitor;
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>;
std::string inline translateHostStateName(std::string_view state)
{
std::size_t found = state.find_last_of('.');
return std::string("HostState:") + std::string(state.substr(found + 1));
}
std::string inline translateOSStatus(std::string_view status)
{
std::size_t found = status.find_last_of('.');
return std::string("OSStatus:") + std::string(status.substr(found + 1));
}
std::string inline queryHostState(sdbusplus::bus::bus& bus,
const btm::psm::PSMConfig& psmConfig)
{
std::string hostState;
BasicVariantType result;
auto method =
bus.new_method_call(psmConfig.hostState.serviceName.c_str(), // Service
psmConfig.hostState.objectPath.c_str(), // Path
"org.freedesktop.DBus.Properties", // Iface
"Get"); // Function
method.append("xyz.openbmc_project.State.Host", "CurrentHostState");
try
{
bus.call(method).read(result);
hostState = std::get<std::string>(result);
}
catch (const sdbusplus::exception::SdBusError& e)
{
fmt::print(
stderr,
"Failed to query `CurrentHostState` with busctl get-property {} {} "
"xyz.openbmc_project.State.Host CurrentHostState"
". Got error: {}\n",
psmConfig.hostState.serviceName, psmConfig.hostState.objectPath,
e.what());
}
return hostState;
}
std::string inline queryOSStatus(sdbusplus::bus::bus& bus,
const btm::psm::PSMConfig& psmConfig)
{
std::vector<std::string> errors;
for (const auto& service : psmConfig.possibleOsStatus)
{
auto method =
bus.new_method_call(service.serviceName.c_str(), // Service
service.objectPath.c_str(), // Path
"org.freedesktop.DBus.Properties", // Iface
"Get"); // Function
method.append("xyz.openbmc_project.State.OperatingSystem.Status",
"OperatingSystemState");
try
{
BasicVariantType result;
bus.call(method).read(result);
return std::get<std::string>(result);
}
catch (const sdbusplus::exception::SdBusError& e)
{
std::string errorMessage = fmt::format(
"busctl get-property {} {} "
"xyz.openbmc_project.State.OperatingSystem.Status OperatingSystemState."
"Got error: {}\n",
service.serviceName, service.objectPath, e.what());
errors.push_back(errorMessage);
}
}
// If we reach here, all attempts have failed. Print all errors.
fmt::print(
stderr,
"Failed to query OperatingSystemState, the attempts are as followed:\n");
for (const auto& error : errors)
{
fmt::print(stderr, "{}\n", error);
}
return "";
}
std::string inline queryNameOwner(sdbusplus::bus::bus& bus,
const btm::psm::DbusServiceLocation& service)
{
std::string ownerUniqueName;
try
{
auto method =
bus.new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus", "GetNameOwner");
method.append(std::string(service.serviceName));
bus.call(method).read(ownerUniqueName);
}
catch (const sdbusplus::exception::SdBusError& e)
{
// This name isn't owned by anyone right now, so we can ignore this
// signal.
}
return ownerUniqueName;
}
Handler::Handler(sdbusplus::bus::bus& bus, const btm::NodeConfig& nodeConfig,
const btm::psm::PSMConfig& psmConfig,
std::shared_ptr<btm::api::IBoottimeApi> api) :
mNodeConfig(nodeConfig), mApi(std::move(api))
{
mPreHostState = queryHostState(bus, psmConfig);
fmt::print("{} `CurrentHostState` is `{}`\n", mNodeConfig.node_name,
mPreHostState);
mHostStateWatcher = std::make_unique<sdbusplus::bus::match::match>(
bus,
sdbusplus::bus::match::rules::propertiesChanged(
psmConfig.hostState.objectPath, "xyz.openbmc_project.State.Host"),
[this](sdbusplus::message::message& message) {
hostStateWatcherCallback(message);
});
mPreOSStatus = queryOSStatus(bus, psmConfig);
fmt::print("{} `OperatingSystemState` is `{}`\n", mNodeConfig.node_name,
mPreOSStatus);
for (const auto& service : psmConfig.possibleOsStatus)
{
mOSStatusWatcher.emplace_back(
std::make_unique<sdbusplus::bus::match::match>(
bus,
sdbusplus::bus::match::rules::propertiesChanged(
service.objectPath,
"xyz.openbmc_project.State.OperatingSystem.Status"),
[this, &bus, service](sdbusplus::message::message& message) {
const std::string& signalSenderUniqueName = message.get_sender();
std::string ownerUniqueName = queryNameOwner(bus, service);
// Compare the signal's sender to the name's rightful owner.
if (signalSenderUniqueName != ownerUniqueName)
{
return; // The signal came from a different process. Ignore it.
}
oSStatusWatcherCallback(message);
}));
}
}
void Handler::hostStateWatcherCallback(sdbusplus::message::message& message)
{
fmt::print(stdout, "[mHostStateWatcher] Signal from {}\n",
mNodeConfig.node_name);
std::string objectName;
boost::container::flat_map<
std::string, std::variant<std::string, bool, int64_t, uint64_t, double>>
values;
message.read(objectName, values);
auto findState = values.find("CurrentHostState");
if (findState != values.end())
{
const std::string curHostState =
std::get<std::string>(findState->second);
// NOTE: Some BMC may emit the same status on change.
if (curHostState == mPreHostState)
{
return;
}
fmt::print(
stdout,
"[mHostStateWatcher] {} `CurrentHostState` has changed from {} to {}\n",
mNodeConfig.node_name, mPreHostState, curHostState);
absl::Status status = mApi->SetNodeCheckpoint(
mNodeConfig, translateHostStateName(curHostState), 0, 0);
btm::log::LogIfError(status);
mPreHostState = curHostState;
fmt::print(
stdout,
"[mHostStateWatcher] {} `CurrentHostState` {} checkpoint has been recorded\n",
mNodeConfig.node_name, curHostState);
}
}
void Handler::oSStatusWatcherCallback(sdbusplus::message::message& message)
{
fmt::print(stdout, "[mOSStatusWatcher] Signal from {}.\n",
mNodeConfig.node_name);
std::string objectName;
boost::container::flat_map<
std::string, std::variant<std::string, bool, int64_t, uint64_t, double>>
values;
message.read(objectName, values);
auto findState = values.find("OperatingSystemState");
if (findState != values.end())
{
const std::string curOSStatus =
std::get<std::string>(findState->second);
// NOTE: Some BMC may emit the same status on change.
if (curOSStatus == mPreOSStatus)
{
return;
}
fmt::print(
stdout,
"[mOSStatusWatcher] {} `OperatingSystemState` has changed from {} to {}\n",
mNodeConfig.node_name, mPreOSStatus, curOSStatus);
absl::Status status = mApi->SetNodeCheckpoint(
mNodeConfig, translateOSStatus(curOSStatus), 0, 0);
btm::log::LogIfError(status);
mPreOSStatus = curOSStatus;
fmt::print(
stdout,
"[mOSStatusWatcher] {} `OperatingSystemState` {} checkpoint has been recorded\n",
mNodeConfig.node_name, curOSStatus);
}
}
} // namespace psm
} // namespace boot_time_monitor