blob: b87370208db03a0a4273585562f0547f115b3324 [file] [log] [blame]
#include "config.h"
#include "activation.hpp"
#include "item_updater.hpp"
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/exception.hpp>
#include <sdbusplus/server.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <filesystem>
#ifdef WANT_SIGNATURE_VERIFY
#include "image_verify.hpp"
#endif
namespace openpower
{
namespace software
{
namespace updater
{
namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
#ifdef WANT_SIGNATURE_VERIFY
// Field mode path and interface.
constexpr auto FIELDMODE_PATH("/xyz/openbmc_project/software");
constexpr auto FIELDMODE_INTERFACE("xyz.openbmc_project.Control.FieldMode");
#endif
constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
void Activation::subscribeToSystemdSignals()
{
auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
SYSTEMD_INTERFACE, "Subscribe");
try
{
this->bus.call_noreply(method);
}
catch (const sdbusplus::exception_t& e)
{
if (e.name() != nullptr &&
strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0)
{
// If an Activation attempt fails, the Unsubscribe method is not
// called. This may lead to an AlreadySubscribed error if the
// Activation is re-attempted.
}
else
{
log<level::ERR>("Error subscribing to systemd",
entry("ERROR=%s", e.what()));
}
}
return;
}
void Activation::unsubscribeFromSystemdSignals()
{
auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
SYSTEMD_INTERFACE, "Unsubscribe");
this->bus.call_noreply(method);
return;
}
auto Activation::requestedActivation(RequestedActivations value)
-> RequestedActivations
{
if ((value == softwareServer::Activation::RequestedActivations::Active) &&
(softwareServer::Activation::requestedActivation() !=
softwareServer::Activation::RequestedActivations::Active))
{
if ((softwareServer::Activation::activation() ==
softwareServer::Activation::Activations::Ready) ||
(softwareServer::Activation::activation() ==
softwareServer::Activation::Activations::Failed))
{
activation(softwareServer::Activation::Activations::Activating);
}
}
return softwareServer::Activation::requestedActivation(value);
}
void Activation::deleteImageManagerObject()
{
// Get the Delete object for <versionID> inside image_manager
constexpr auto versionServiceStr = "xyz.openbmc_project.Software.Version";
constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete";
std::string versionService;
auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
MAPPER_INTERFACE, "GetObject");
method.append(path);
method.append(std::vector<std::string>({deleteInterface}));
std::map<std::string, std::vector<std::string>> mapperResponse;
try
{
auto mapperResponseMsg = bus.call(method);
mapperResponseMsg.read(mapperResponse);
if (mapperResponse.begin() == mapperResponse.end())
{
log<level::ERR>("ERROR in reading the mapper response",
entry("VERSIONPATH=%s", path.c_str()));
return;
}
}
catch (const sdbusplus::exception_t& e)
{
log<level::ERR>("Error in Get Delete Object",
entry("VERSIONPATH=%s", path.c_str()));
return;
}
// We need to find the phosphor-software-manager's version service
// to invoke the delete interface
for (auto resp : mapperResponse)
{
if (resp.first.find(versionServiceStr) != std::string::npos)
{
versionService = resp.first;
}
}
if (versionService.empty())
{
log<level::ERR>("Error finding version service");
return;
}
// Call the Delete object for <versionID> inside image_manager
method = this->bus.new_method_call(versionService.c_str(), path.c_str(),
deleteInterface, "Delete");
try
{
bus.call(method);
}
catch (const sdbusplus::exception_t& e)
{
if (e.name() != nullptr && strcmp("System.Error.ELOOP", e.name()) == 0)
{
// TODO: Error being tracked with openbmc/openbmc#3311
}
else
{
log<level::ERR>("Error performing call to Delete object path",
entry("ERROR=%s", e.what()),
entry("PATH=%s", path.c_str()));
}
return;
}
}
bool Activation::checkApplyTimeImmediate()
{
auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf);
if (service.empty())
{
log<level::INFO>("Error getting the service name for Host image "
"ApplyTime. The Host needs to be manually rebooted to "
"complete the image activation if needed "
"immediately.");
}
else
{
auto method = bus.new_method_call(service.c_str(), applyTimeObjPath,
dbusPropIntf, "Get");
method.append(applyTimeIntf, applyTimeProp);
try
{
auto reply = bus.call(method);
std::variant<std::string> result;
reply.read(result);
auto applyTime = std::get<std::string>(result);
if (applyTime == applyTimeImmediate)
{
return true;
}
}
catch (const sdbusplus::exception_t& e)
{
log<level::ERR>("Error in getting ApplyTime",
entry("ERROR=%s", e.what()));
}
}
return false;
}
void Activation::rebootHost()
{
auto service = utils::getService(bus, hostStateObjPath, hostStateIntf);
if (service.empty())
{
log<level::ALERT>("Error in getting the service name to reboot the "
"Host. The Host needs to be manually rebooted to "
"complete the image activation.");
}
auto method = bus.new_method_call(service.c_str(), hostStateObjPath,
dbusPropIntf, "Set");
std::variant<std::string> hostReboot = hostStateRebootVal;
method.append(hostStateIntf, hostStateRebootProp, hostReboot);
try
{
auto reply = bus.call(method);
}
catch (const sdbusplus::exception_t& e)
{
log<level::ALERT>("Error in trying to reboot the Host. "
"The Host needs to be manually rebooted to complete "
"the image activation.",
entry("ERROR=%s", e.what()));
report<InternalFailure>();
}
}
uint8_t RedundancyPriority::priority(uint8_t value)
{
parent.parent.freePriority(value, parent.versionId);
return softwareServer::RedundancyPriority::priority(value);
}
#ifdef WANT_SIGNATURE_VERIFY
bool Activation::validateSignature(const std::string& pnorFileName)
{
using Signature = openpower::software::image::Signature;
std::filesystem::path imageDir(IMG_DIR);
Signature signature(imageDir / versionId, pnorFileName,
PNOR_SIGNED_IMAGE_CONF_PATH);
// Validate the signed image.
if (signature.verify())
{
return true;
}
// Log error and continue activation process, if field mode disabled.
log<level::ERR>("Error occurred during image validation");
report<InternalFailure>();
try
{
if (!fieldModeEnabled())
{
return true;
}
}
catch (const InternalFailure& e)
{
report<InternalFailure>();
}
return false;
}
bool Activation::fieldModeEnabled()
{
auto fieldModeSvc =
utils::getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE);
auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH,
"org.freedesktop.DBus.Properties", "Get");
method.append(FIELDMODE_INTERFACE, "FieldModeEnabled");
std::variant<bool> fieldMode;
try
{
auto reply = bus.call(method);
reply.read(fieldMode);
return std::get<bool>(fieldMode);
}
catch (const sdbusplus::exception_t& e)
{
log<level::ERR>("Error in fieldModeEnabled getValue");
elog<InternalFailure>();
}
}
#endif
} // namespace updater
} // namespace software
} // namespace openpower