introduce PersistentState manager
This commit brings new mechanism to store application state over system
reboot. State is saved to json file and restored on application startup
by easy to use class.
Currently only PowerState is managed by this class but more states can
be added in future.
Tested: verify that power state saved to file and restored on startup
Change-Id: I91fb46f262827dda89f438eefea38d59e02ba25c
Signed-off-by: Andrei Kartashev <a.kartashev@yadro.com>
diff --git a/meson.build b/meson.build
index f0c8ec8..4507df2 100644
--- a/meson.build
+++ b/meson.build
@@ -41,6 +41,7 @@
executable(
'power-control',
'src/power_control.cpp',
+ include_directories: include_directories('src'),
cpp_args: cpp_args,
dependencies: deps,
install: true,
@@ -57,4 +58,4 @@
install_data(
'config/power-config-host0.json',
- install_dir: '/usr/share/x86-power-control/')
\ No newline at end of file
+ install_dir: '/usr/share/x86-power-control/')
diff --git a/src/power_control.cpp b/src/power_control.cpp
index fbe1266..b99556a 100644
--- a/src/power_control.cpp
+++ b/src/power_control.cpp
@@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
*/
+#include "power_control.hpp"
+
#include <sys/sysinfo.h>
#include <systemd/sd-journal.h>
@@ -34,6 +36,7 @@
{
static boost::asio::io_service io;
std::shared_ptr<sdbusplus::asio::connection> conn;
+PersistentState appState;
static std::string node = "0";
@@ -135,8 +138,6 @@
{"WarmResetCheckMs", 500},
{"PowerOffSaveMs", 7000},
{"SlotPowerCycleMs", 200}};
-const static std::filesystem::path powerControlDir = "/var/lib/power-control";
-const static constexpr std::string_view powerStateFile = "power-state";
static bool nmiEnabled = true;
static bool sioEnabled = true;
@@ -544,8 +545,8 @@
}
return;
}
- std::ofstream powerStateStream(powerControlDir / powerStateFile);
- powerStateStream << getChassisState(state);
+ appState.set(PersistentState::Params::PowerState,
+ std::string{getChassisState(state)});
});
}
static void setPowerState(const PowerState state)
@@ -731,7 +732,7 @@
"OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
}
-static int initializePowerStateStorage()
+PersistentState::PersistentState()
{
// create the power control directory if it doesn't exist
std::error_code ec;
@@ -741,29 +742,90 @@
{
lg2::error("failed to create {DIR_NAME}: {ERROR_MSG}", "DIR_NAME",
powerControlDir.string(), "ERROR_MSG", ec.message());
- return -1;
+ throw std::runtime_error("Failed to create state directory");
}
}
- // Create the power state file if it doesn't exist
- if (!std::filesystem::exists(powerControlDir / powerStateFile))
+
+ // read saved state, it's ok, if the file doesn't exists
+ std::ifstream appStateStream(powerControlDir / stateFile);
+ if (!appStateStream.is_open())
{
- std::ofstream powerStateStream(powerControlDir / powerStateFile);
- powerStateStream << getChassisState(powerState);
+ lg2::info("Cannot open state file \'{PATH}\'", "PATH",
+ std::string(powerControlDir / stateFile));
+ stateData = nlohmann::json({});
+ return;
}
- return 0;
+ try
+ {
+ appStateStream >> stateData;
+ if (stateData.is_discarded())
+ {
+ lg2::info("Cannot parse state file \'{PATH}\'", "PATH",
+ std::string(powerControlDir / stateFile));
+ stateData = nlohmann::json({});
+ return;
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ lg2::info("Cannot read state file \'{PATH}\'", "PATH",
+ std::string(powerControlDir / stateFile));
+ stateData = nlohmann::json({});
+ return;
+ }
+}
+PersistentState::~PersistentState()
+{
+ saveState();
+}
+const std::string PersistentState::get(Params parameter)
+{
+ auto val = stateData.find(getName(parameter));
+ if (val != stateData.end())
+ {
+ return val->get<std::string>();
+ }
+ return getDefault(parameter);
+}
+void PersistentState::set(Params parameter, const std::string& value)
+{
+ stateData[getName(parameter)] = value;
+ saveState();
+}
+
+const std::string PersistentState::getName(const Params parameter)
+{
+ switch (parameter)
+ {
+ case Params::PowerState:
+ return "PowerState";
+ }
+ return "";
+}
+const std::string PersistentState::getDefault(const Params parameter)
+{
+ switch (parameter)
+ {
+ case Params::PowerState:
+ return "xyz.openbmc_project.State.Chassis.PowerState.Off";
+ }
+ return "";
+}
+void PersistentState::saveState()
+{
+ std::ofstream appStateStream(powerControlDir / stateFile, std::ios::trunc);
+ if (!appStateStream.is_open())
+ {
+ lg2::error("Cannot write state file \'{PATH}\'", "PATH",
+ std::string(powerControlDir / stateFile));
+ return;
+ }
+ appStateStream << stateData.dump(indentationSize);
}
static bool wasPowerDropped()
{
- std::ifstream powerStateStream(powerControlDir / powerStateFile);
- if (!powerStateStream.is_open())
- {
- lg2::error("Failed to open power state file");
- return false;
- }
-
- std::string state;
- std::getline(powerStateStream, state);
+ std::string state = appState.get(PersistentState::Params::PowerState);
return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
}
@@ -2693,12 +2755,6 @@
powerState = PowerState::on;
}
}
- // Initialize the power state storage
- if (initializePowerStateStorage() < 0)
- {
- return -1;
- }
-
// Check if we need to start the Power Restore policy
powerRestorePolicyCheck();
diff --git a/src/power_control.hpp b/src/power_control.hpp
new file mode 100644
index 0000000..5dda797
--- /dev/null
+++ b/src/power_control.hpp
@@ -0,0 +1,95 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright (C) 2021-2022 YADRO.
+ */
+
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+#include <filesystem>
+#include <string_view>
+
+namespace power_control
+{
+
+/**
+ * @brief Persistent State Manager
+ *
+ * This manager supposed to store runtime parameters that supposed to be
+ * persistent over BMC reboot. It provides simple Get/Set interface and handle
+ * default values, hardcoded in getDefault() method.
+ * @note: currently only string parameters supported
+ */
+class PersistentState
+{
+ public:
+ /**
+ * List of all supported parameters
+ */
+ enum class Params
+ {
+ PowerState,
+ };
+
+ /**
+ * @brief Persistent storage initialization
+ *
+ * Class constructor automatically load last state from JSON file
+ */
+ PersistentState();
+ /**
+ * @brief Persistent storage cleanup
+ *
+ * Class destructor automatically save state to JSON file
+ */
+ ~PersistentState();
+ /**
+ * @brief Get parameter value from the storage
+ *
+ * Get the parameter from cached storage. Default value returned, if
+ * parameter was not set before.
+ * @param parameter - parameter to get
+ * @return parameter value
+ */
+ const std::string get(Params parameter);
+ /**
+ * @brief Store parameter value
+ *
+ * Set the parameter value in cached storage and dump it to disk.
+ * @param parameter - parameter to set
+ * @param value - parameter value to assign
+ */
+ void set(Params parameter, const std::string& value);
+
+ private:
+ nlohmann::json stateData;
+ const std::filesystem::path powerControlDir = "/var/lib/power-control";
+ const std::string_view stateFile = "state.json";
+ const int indentationSize = 2;
+
+ /**
+ * @brief Covert parameter ID to name
+ *
+ * Get the name corresponding to the given parameter.
+ * String name only used by the manager internal to generate human-readable
+ * JSON.
+ * @param parameter - parameter to convert
+ * @return parameter name
+ */
+ const std::string getName(const Params parameter);
+ /**
+ * @brief Get default parameter value
+ *
+ * Get the default value, associated with given parameter.
+ * @param parameter - parameter to get
+ * @return parameter default value
+ */
+ const std::string getDefault(const Params parameter);
+ /**
+ * @brief Save cache to file on disk
+ */
+ void saveState();
+};
+
+} // namespace power_control