blob: c4b3e7b6bc4a23325fd83d3dc02fe151d61a34e2 [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
Andrew Geissler0d1c3f12021-07-27 16:21:01 -04003#include "host_check.hpp"
4
Andrew Geissler0971dea2017-03-28 14:32:40 -05005#include <unistd.h>
Andrew Geisslere426b582020-05-28 12:40:55 -05006
Andrew Geissler8ffdb262021-09-20 15:25:19 -05007#include <phosphor-logging/lg2.hpp>
Andrew Geisslerb09463d2017-03-24 15:55:17 -05008#include <sdbusplus/bus.hpp>
Anthony Wilson32c532e2018-10-25 21:56:07 -05009#include <sdbusplus/exception.hpp>
Patrick Williams9a286db2024-01-17 06:29:47 -060010#include <xyz/openbmc_project/Condition/HostFirmware/client.hpp>
11#include <xyz/openbmc_project/ObjectMapper/client.hpp>
12#include <xyz/openbmc_project/State/Chassis/client.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050013
14#include <cstdio>
15#include <cstdlib>
Patrick Williams78c066f2024-02-13 12:25:58 -060016#include <format>
Andrew Geisslere426b582020-05-28 12:40:55 -050017#include <fstream>
18#include <iostream>
Patrick Williamse9040bb2024-01-30 14:12:44 -060019#include <ranges>
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050020#include <thread>
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050021#include <vector>
Andrew Geisslerb09463d2017-03-24 15:55:17 -050022
Andrew Geissler8ffdb262021-09-20 15:25:19 -050023namespace phosphor
24{
25namespace state
26{
27namespace manager
28{
29
30PHOSPHOR_LOG2_USING;
31
Andrew Geisslereeaccf82017-03-28 15:23:27 -050032using namespace std::literals;
Patrick Williams9a286db2024-01-17 06:29:47 -060033
34using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
35using Chassis = sdbusplus::client::xyz::openbmc_project::state::Chassis<>;
36using HostFirmware =
37 sdbusplus::client::xyz::openbmc_project::condition::HostFirmware<>;
Andrew Geissler0971dea2017-03-28 14:32:40 -050038
Andrew Geisslereeaccf82017-03-28 15:23:27 -050039// Required strings for sending the msg to check on host
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050040constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition";
41constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
Andrew Geissler0971dea2017-03-28 14:32:40 -050042
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040043constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis";
Andrew Geissler0d1c3f12021-07-27 16:21:01 -040044constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState";
45
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050046// Find all implementations of Condition interface and check if host is
47// running over it
Patrick Williamsf053e6f2022-07-22 19:26:54 -050048bool checkFirmwareConditionRunning(sdbusplus::bus_t& bus)
Andrew Geissler0971dea2017-03-28 14:32:40 -050049{
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050050 // Find all implementations of host firmware condition interface
Patrick Williams9a286db2024-01-17 06:29:47 -060051 auto mapper = bus.new_method_call(ObjectMapper::default_service,
52 ObjectMapper::instance_path,
53 ObjectMapper::interface, "GetSubTree");
Andrew Geisslereeaccf82017-03-28 15:23:27 -050054
Patrick Williams9a286db2024-01-17 06:29:47 -060055 mapper.append("/", 0, std::vector<std::string>({HostFirmware::interface}));
Andrew Geisslereeaccf82017-03-28 15:23:27 -050056
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050057 std::map<std::string, std::map<std::string, std::vector<std::string>>>
58 mapperResponse;
Anthony Wilson32c532e2018-10-25 21:56:07 -050059
60 try
61 {
62 auto mapperResponseMsg = bus.call(mapper);
63 mapperResponseMsg.read(mapperResponse);
64 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050065 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -050066 {
Andrew Geisslerad65b2d2021-09-21 12:53:29 -050067 error(
68 "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}",
69 "ERROR", e);
Anthony Wilson32c532e2018-10-25 21:56:07 -050070 throw;
Andrew Geisslereeaccf82017-03-28 15:23:27 -050071 }
72
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050073 if (mapperResponse.empty())
74 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050075 info("Mapper response for HostFirmware conditions is empty!");
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050076 return false;
77 }
78
79 // Now read the CurrentFirmwareCondition from all interfaces we found
Andrew Geisslercd0ebe82021-05-14 21:39:51 -050080 // Currently there are two implementations of this interface. One by IPMI
81 // and one by PLDM. The IPMI interface does a realtime check with the host
82 // when the interface is called. This means if the host is not running,
83 // we will have to wait for the timeout (currently set to 3 seconds). The
84 // PLDM interface reads a cached state. The PLDM service does not put itself
85 // on D-Bus until it has checked with the host. Therefore it's most
86 // efficient to call the PLDM interface first. Do that by going in reverse
87 // of the interfaces returned to us (PLDM will be last if available)
Patrick Williamse9040bb2024-01-30 14:12:44 -060088 for (const auto& [path, services] : std::views::reverse(mapperResponse))
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050089 {
90 for (const auto& serviceIter : services)
91 {
92 const std::string& service = serviceIter.first;
93
94 try
95 {
96 auto method = bus.new_method_call(service.c_str(), path.c_str(),
97 PROPERTY_INTERFACE, "Get");
Patrick Williams9a286db2024-01-17 06:29:47 -060098 method.append(HostFirmware::interface, CONDITION_HOST_PROPERTY);
Andrew Geissler2d7b69e2021-05-06 13:12:45 -050099
100 auto response = bus.call(method);
Patrick Williams9a286db2024-01-17 06:29:47 -0600101 std::variant<HostFirmware::FirmwareCondition> currentFwCondV;
NodeMan9780db4752022-05-04 09:06:40 -0500102 response.read(currentFwCondV);
103 auto currentFwCond =
Patrick Williams9a286db2024-01-17 06:29:47 -0600104 std::get<HostFirmware::FirmwareCondition>(currentFwCondV);
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500105
NodeMan97ef828c42022-05-31 11:24:32 -0500106 info(
107 "Read host fw condition {COND_VALUE} from {COND_SERVICE}, {COND_PATH}",
108 "COND_VALUE", currentFwCond, "COND_SERVICE", service,
109 "COND_PATH", path);
110
Patrick Williams9a286db2024-01-17 06:29:47 -0600111 if (currentFwCond == HostFirmware::FirmwareCondition::Running)
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500112 {
113 return true;
114 }
115 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500116 catch (const sdbusplus::exception_t& e)
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500117 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500118 error("Error reading HostFirmware condition, error: {ERROR}, "
119 "service: {SERVICE} path: {PATH}",
120 "ERROR", e, "SERVICE", service, "PATH", path);
Andrew Geissler2d7b69e2021-05-06 13:12:45 -0500121 throw;
122 }
123 }
124 }
125 return false;
Andrew Geisslereeaccf82017-03-28 15:23:27 -0500126}
127
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400128// Helper function to check if chassis power is on
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500129bool isChassiPowerOn(sdbusplus::bus_t& bus, size_t id)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400130{
Potin Lai07e73aa2022-03-29 15:16:55 +0800131 auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id);
Patrick Williams9a286db2024-01-17 06:29:47 -0600132 auto objpath = std::string{Chassis::namespace_path::value} + "/" +
133 std::string{Chassis::namespace_path::chassis} +
134 std::to_string(id);
Potin Lai70f36d82022-03-15 10:25:39 +0800135
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400136 try
137 {
Potin Lai70f36d82022-03-15 10:25:39 +0800138 auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(),
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400139 PROPERTY_INTERFACE, "Get");
Patrick Williams9a286db2024-01-17 06:29:47 -0600140 method.append(Chassis::interface, CHASSIS_STATE_POWER_PROP);
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400141
142 auto response = bus.call(method);
Patrick Williams9a286db2024-01-17 06:29:47 -0600143 std::variant<Chassis::PowerState> currentPowerStateV;
NodeMan9780db4752022-05-04 09:06:40 -0500144 response.read(currentPowerStateV);
NodeMan97ef828c42022-05-31 11:24:32 -0500145
Patrick Williams9a286db2024-01-17 06:29:47 -0600146 auto currentPowerState =
147 std::get<Chassis::PowerState>(currentPowerStateV);
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400148
Patrick Williams9a286db2024-01-17 06:29:47 -0600149 if (currentPowerState == Chassis::PowerState::On)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400150 {
151 return true;
152 }
153 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500154 catch (const sdbusplus::exception_t& e)
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400155 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500156 error("Error reading Chassis Power State, error: {ERROR}, "
157 "service: {SERVICE} path: {PATH}",
Potin Lai70f36d82022-03-15 10:25:39 +0800158 "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str());
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400159 throw;
160 }
161 return false;
162}
163
Potin Lai70f36d82022-03-15 10:25:39 +0800164bool isHostRunning(size_t id)
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500165{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500166 info("Check if host is running");
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500167
Andrew Geissler0971dea2017-03-28 14:32:40 -0500168 auto bus = sdbusplus::bus::new_default();
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500169
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400170 // No need to check if chassis power is not on
Potin Lai70f36d82022-03-15 10:25:39 +0800171 if (!isChassiPowerOn(bus, id))
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400172 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500173 info("Chassis power not on, exit");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400174 return false;
175 }
176
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500177 // This applications systemd service is setup to only run after all other
178 // application that could possibly implement the needed interface have
179 // been started. However, the use of mapper to find those interfaces means
180 // we have a condition where the interface may be on D-Bus but not stored
NodeMan97ef828c42022-05-31 11:24:32 -0500181 // within mapper yet. There are five built in retries to check if it's
182 // found the host is not up. This service is only called if chassis power
Manojkiran Eda3ff5a362024-06-17 10:59:07 +0530183 // is on when the BMC comes up, so this won't impact most normal cases
NodeMan97ef828c42022-05-31 11:24:32 -0500184 // where the BMC is rebooted with chassis power off. In cases where
185 // chassis power is on, the host is likely running so we want to be sure
186 // we check all interfaces
187 for (int i = 0; i < 5; i++)
Andrew Geissler0971dea2017-03-28 14:32:40 -0500188 {
NodeMan97ef828c42022-05-31 11:24:32 -0500189 debug(
190 "Introspecting new bus objects for bus id: {ID} sleeping for 1 second.",
191 "ID", id);
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500192 // Give mapper a small window to introspect new objects on bus
NodeMan97ef828c42022-05-31 11:24:32 -0500193 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500194 if (checkFirmwareConditionRunning(bus))
195 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500196 info("Host is running!");
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500197 // Create file for host instance and create in filesystem to
198 // indicate to services that host is running
Patrick Williams78c066f2024-02-13 12:25:58 -0600199 std::string hostFile = std::format(HOST_RUNNING_FILE, 0);
200 std::ofstream outfile(hostFile);
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500201 outfile.close();
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400202 return true;
Andrew Geisslercd0ebe82021-05-14 21:39:51 -0500203 }
Andrew Geissler07d0ed52017-03-28 15:44:18 -0500204 }
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500205 info("Host is not running!");
Andrew Geissler0d1c3f12021-07-27 16:21:01 -0400206 return false;
Andrew Geisslerb09463d2017-03-24 15:55:17 -0500207}
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500208
209} // namespace manager
210} // namespace state
211} // namespace phosphor