| #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 |