blob: fe94f728ae6f922b3ab3ce29caa02731fc3b21cd [file] [log] [blame]
#include "config.h"
#include <unistd.h>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/exception.hpp>
#include <xyz/openbmc_project/Logging/Create/server.hpp>
#include <xyz/openbmc_project/Logging/Entry/server.hpp>
#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
#include <cstdlib>
#include <fstream>
#include <string>
namespace phosphor
{
namespace state
{
namespace manager
{
PHOSPHOR_LOG2_USING;
constexpr auto HOST_STATE_SVC = "xyz.openbmc_project.State.Host";
constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
constexpr auto BOOT_STATE_INTF = "xyz.openbmc_project.State.Boot.Progress";
constexpr auto BOOT_PROGRESS_PROP = "BootProgress";
constexpr auto LOGGING_SVC = "xyz.openbmc_project.Logging";
constexpr auto LOGGING_PATH = "/xyz/openbmc_project/logging";
constexpr auto LOGGING_CREATE_INTF = "xyz.openbmc_project.Logging.Create";
constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
constexpr auto HOST_STATE_QUIESCE_TGT = "obmc-host-quiesce@0.target";
bool wasHostBooting(sdbusplus::bus_t& bus)
{
try
{
using ProgressStages = sdbusplus::xyz::openbmc_project::State::Boot::
server::Progress::ProgressStages;
auto method = bus.new_method_call(HOST_STATE_SVC, HOST_STATE_PATH,
PROPERTY_INTERFACE, "Get");
method.append(BOOT_STATE_INTF, BOOT_PROGRESS_PROP);
auto response = bus.call(method);
std::variant<ProgressStages> bootProgressV;
response.read(bootProgressV);
auto bootProgress = std::get<ProgressStages>(bootProgressV);
if (bootProgress == ProgressStages::Unspecified)
{
info("Host was not booting before BMC reboot");
return false;
}
info("Host was booting before BMC reboot: {BOOTPROGRESS}",
"BOOTPROGRESS", bootProgress);
}
catch (const sdbusplus::exception_t& e)
{
error("Error reading BootProgress, error {ERROR}, service {SERVICE}, "
"path {PATH}",
"ERROR", e, "SERVICE", HOST_STATE_SVC, "PATH", HOST_STATE_PATH);
throw;
}
return true;
}
void createErrorLog(sdbusplus::bus_t& bus)
{
try
{
// Create interface requires something for additionalData
std::map<std::string, std::string> additionalData;
additionalData.emplace("_PID", std::to_string(getpid()));
static constexpr auto errorMessage =
"xyz.openbmc_project.State.Error.HostNotRunning";
auto method = bus.new_method_call(LOGGING_SVC, LOGGING_PATH,
LOGGING_CREATE_INTF, "Create");
auto level =
sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
Error);
method.append(errorMessage, level, additionalData);
auto resp = bus.call(method);
}
catch (const sdbusplus::exception_t& e)
{
error(
"sdbusplus D-Bus call exception, error {ERROR}, objpath {OBJPATH}, "
"interface {INTERFACE}",
"ERROR", e, "OBJPATH", LOGGING_PATH, "INTERFACE",
LOGGING_CREATE_INTF);
throw std::runtime_error(
"Error in invoking D-Bus logging create interface");
}
catch (const std::exception& e)
{
error("D-bus call exception: {ERROR}", "ERROR", e);
throw e;
}
}
// Once CHASSIS_ON_FILE is removed, the obmc-chassis-poweron@.target has
// completed and the phosphor-chassis-state-manager code has processed it.
bool isChassisTargetComplete()
{
auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
size++; // null
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, CHASSIS_ON_FILE, 0);
std::ifstream f(buf.get());
return !f.good();
}
void moveToHostQuiesce(sdbusplus::bus_t& bus)
{
try
{
auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
SYSTEMD_INTERFACE, "StartUnit");
method.append(HOST_STATE_QUIESCE_TGT);
method.append("replace");
bus.call_noreply(method);
}
catch (const sdbusplus::exception_t& e)
{
error("sdbusplus call exception starting quiesce target: {ERROR}",
"ERROR", e);
throw std::runtime_error(
"Error in invoking D-Bus systemd StartUnit method");
}
}
} // namespace manager
} // namespace state
} // namespace phosphor
int main()
{
using namespace phosphor::state::manager;
PHOSPHOR_LOG2_USING;
auto bus = sdbusplus::bus::new_default();
// Chassis power is on if this service starts but need to wait for the
// obmc-chassis-poweron@.target to complete before potentially initiating
// another systemd target transition (i.e. Quiesce->Reboot)
while (!isChassisTargetComplete())
{
debug("Waiting for chassis on target to complete");
std::this_thread::sleep_for(std::chrono::seconds(1));
// There is no timeout here, wait until it happens or until system
// is powered off and this service is stopped
}
info("Chassis power on has completed, checking if host is "
"still running after the BMC reboot");
// Check the last BootProgeress to see if the host was booting before
// the BMC reboot occurred
if (!wasHostBooting(bus))
{
return 0;
}
// Host was booting before the BMC reboot so log an error and go to host
// quiesce target
createErrorLog(bus);
moveToHostQuiesce(bus);
return 0;
}