blob: 1bffe24c06c57d5b426d020fafa8a3ea152003d3 [file] [log] [blame]
/**
* Copyright © 2020 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "manager.hpp"
#include "chassis.hpp"
#include "config_file_parser.hpp"
#include "exception_utils.hpp"
#include "rule.hpp"
#include "utility.hpp"
#include <sdbusplus/bus.hpp>
#include <chrono>
#include <exception>
#include <stdexcept>
#include <tuple>
#include <utility>
#include <variant>
namespace phosphor::power::regulators
{
namespace fs = std::filesystem;
/**
* Standard configuration file directory. This directory is part of the
* firmware install image. It contains the standard version of the config file.
*/
const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"};
/**
* Test configuration file directory. This directory can contain a test version
* of the config file. The test version will override the standard version.
*/
const fs::path testConfigFileDir{"/etc/phosphor-regulators"};
Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) :
ManagerObject{bus, objPath, true}, bus{bus}, eventLoop{event}, services{bus}
{
/* Temporarily comment out until D-Bus interface is defined and available.
// Subscribe to interfacesAdded signal for filename property
std::unique_ptr<sdbusplus::server::match::match> matchPtr =
std::make_unique<sdbusplus::server::match::match>(
bus,
sdbusplus::bus::match::rules::interfacesAdded(sysDbusObj).c_str(),
std::bind(std::mem_fn(&Manager::signalHandler), this,
std::placeholders::_1));
signals.emplace_back(std::move(matchPtr));
// Attempt to get the filename property from dbus
setFileName(getFileNameDbus());
*/
// Temporarily hard-code JSON config file name to first system that will use
// this application. Remove this when D-Bus interface is available.
fileName = "ibm_rainier.json";
if (!fileName.empty())
{
loadConfigFile();
}
// Obtain dbus service name
bus.request_name(busName);
}
void Manager::configure()
{
// Verify System object exists; this means config file has been loaded
if (system)
{
// Configure the regulator devices in the system
system->configure(services);
}
else
{
services.getJournal().logError("Unable to configure regulator devices: "
"Configuration file not loaded");
// TODO: Log error
}
// TODO Configuration errors that should halt poweron,
// throw InternalFailure exception (or similar) to
// fail the call(busctl) to this method
}
void Manager::monitor(bool enable)
{
if (enable)
{
/* Temporarily comment out until monitoring is supported.
Timer timer(eventLoop, std::bind(&Manager::timerExpired, this));
// Set timer as a repeating 1sec timer
timer.restart(std::chrono::milliseconds(1000));
timers.emplace_back(std::move(timer));
*/
}
else
{
/* Temporarily comment out until monitoring is supported.
// Delete all timers to disable monitoring
timers.clear();
*/
// Verify System object exists; this means config file has been loaded
if (system)
{
// Close the regulator devices in the system. Monitoring is
// normally disabled because the system is being powered off. The
// devices should be closed in case hardware is removed or replaced
// while the system is at standby.
system->closeDevices(services);
}
}
}
void Manager::timerExpired()
{
// TODO Analyze, refresh sensor status, and
// collect/update telemetry for each regulator
}
void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
const struct signalfd_siginfo* /*sigInfo*/)
{
if (!fileName.empty())
{
loadConfigFile();
}
}
void Manager::signalHandler(sdbusplus::message::message& msg)
{
if (msg)
{
sdbusplus::message::object_path op;
msg.read(op);
if (static_cast<const std::string&>(op) != sysDbusPath)
{
// Object path does not match the path
return;
}
// An interfacesAdded signal returns a dictionary of interface
// names to a dictionary of properties and their values
// https://dbus.freedesktop.org/doc/dbus-specification.html
std::map<std::string, std::map<std::string, std::variant<std::string>>>
intfProp;
msg.read(intfProp);
auto itIntf = intfProp.find(sysDbusIntf);
if (itIntf == intfProp.cend())
{
// Interface not found on the path
return;
}
auto itProp = itIntf->second.find(sysDbusProp);
if (itProp == itIntf->second.cend())
{
// Property not found on the interface
return;
}
// Set fileName and call parse json function
setFileName(std::get<std::string>(itProp->second));
if (!fileName.empty())
{
loadConfigFile();
}
}
}
const std::string Manager::getFileNameDbus()
{
std::string fileName = "";
using namespace phosphor::power::util;
try
{
// Do not log an error when service or property are not found
auto service = getService(sysDbusPath, sysDbusIntf, bus, false);
if (!service.empty())
{
getProperty(sysDbusIntf, sysDbusProp, sysDbusPath, service, bus,
fileName);
}
}
catch (const sdbusplus::exception::SdBusError&)
{
// File name property not available on dbus
fileName = "";
}
return fileName;
}
fs::path Manager::findConfigFile()
{
// First look in the test directory
fs::path pathName{testConfigFileDir / fileName};
if (!fs::exists(pathName))
{
// Look in the standard directory
pathName = standardConfigFileDir / fileName;
if (!fs::exists(pathName))
{
throw std::runtime_error{"Configuration file does not exist: " +
pathName.string()};
}
}
return pathName;
}
void Manager::loadConfigFile()
{
try
{
// Find the absolute path to the config file
fs::path pathName = findConfigFile();
// Log info message in journal; config file path is important
services.getJournal().logInfo("Loading configuration file " +
pathName.string());
// Parse the config file
std::vector<std::unique_ptr<Rule>> rules{};
std::vector<std::unique_ptr<Chassis>> chassis{};
std::tie(rules, chassis) = config_file_parser::parse(pathName);
// Store config file information in a new System object. The old System
// object, if any, is automatically deleted.
system = std::make_unique<System>(std::move(rules), std::move(chassis));
}
catch (const std::exception& e)
{
// Log error messages in journal
services.getJournal().logError(exception_utils::getMessages(e));
services.getJournal().logError("Unable to load configuration file");
// TODO: Create error log entry
}
}
} // namespace phosphor::power::regulators