Handle possible SdBus exceptions
Right now service allows to access several signals using DBus
interface, instead of GPIO pins. In case of that option selected in
config, service will try to read given DBus property using function
getProperty. Unfortunately that attempt might fail, for example when
that specific object doesn't exist yet, which will cause with
unhandled exception and evantually service restart.
This change adds handler for that sort of exception. If read property
fails, another one will be scheduled in the next second. That process
shall be repeated until value is successfully read. As there are
several possible properties to read, each one shall utilize its own
timer, so that every failure can be handled independently from each
other.
Tested:
Change has been tested on Intel platform, which showed symptoms
described above: service tried to get property which has not yet been
created, which caused its crash and restart. With this fix applied,
issue stopped reproducing.
Change-Id: Ib9b89e07a348868d2f15ffda31b3dc9a47340873
Signed-off-by: Michal Orzel <michalx.orzel@intel.com>
diff --git a/config/power-config-host0.json b/config/power-config-host0.json
index f575b15..74e96c9 100644
--- a/config/power-config-host0.json
+++ b/config/power-config-host0.json
@@ -82,7 +82,8 @@
"PsPowerOKWatchdogMs": 8000,
"GracefulPowerOffS": 300,
"WarmResetCheckMs": 500,
- "PowerOffSaveMs": 7000
+ "PowerOffSaveMs": 7000,
+ "DbusGetPropertyRetry": 1000
},
"event_configs": {
"NMIWhenPoweredOff": true
diff --git a/src/power_control.cpp b/src/power_control.cpp
index 913747a..bc30b71 100644
--- a/src/power_control.cpp
+++ b/src/power_control.cpp
@@ -142,7 +142,8 @@
{"GracefulPowerOffS", (5 * 60)},
{"WarmResetCheckMs", 500},
{"PowerOffSaveMs", 7000},
- {"SlotPowerCycleMs", 200}};
+ {"SlotPowerCycleMs", 200},
+ {"DbusGetPropertyRetry", 1000}};
static bool nmiEnabled = true;
static bool nmiWhenPoweredOff = true;
@@ -169,6 +170,10 @@
static boost::asio::steady_timer restartCauseTimer(io);
static boost::asio::steady_timer slotPowerCycleTimer(io);
+// Map containing timers used for D-Bus get-property retries
+static boost::container::flat_map<std::string, boost::asio::steady_timer>
+ dBusRetryTimers;
+
// GPIO Lines and Event Descriptors
static gpiod::line psPowerOKLine;
static boost::asio::posix::stream_descriptor psPowerOKEvent(io);
@@ -2488,24 +2493,41 @@
std::move(pulseEventMatcherCallback));
}
-int getProperty(ConfigData& configData)
-{
- auto method = conn->new_method_call(
- configData.dbusName.c_str(), configData.path.c_str(),
- "org.freedesktop.DBus.Properties", "Get");
- method.append(configData.interface.c_str(), configData.lineName.c_str());
+// D-Bus property read functions
+void reschedulePropertyRead(const ConfigData& configData);
- auto reply = conn->call(method);
- if (reply.is_method_error())
+int getProperty(const ConfigData& configData)
+{
+ std::variant<bool> resp;
+
+ try
{
- lg2::error(
- "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
- "PROPERTY", configData.lineName, "INTERFACE", configData.interface,
- "PATH", configData.path);
+ auto method = conn->new_method_call(
+ configData.dbusName.c_str(), configData.path.c_str(),
+ "org.freedesktop.DBus.Properties", "Get");
+ method.append(configData.interface.c_str(),
+ configData.lineName.c_str());
+
+ auto reply = conn->call(method);
+ if (reply.is_method_error())
+ {
+ lg2::error(
+ "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
+ "PROPERTY", configData.lineName, "INTERFACE",
+ configData.interface, "PATH", configData.path);
+ return -1;
+ }
+
+ reply.read(resp);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
+ configData.lineName, "WHAT", e.what());
+ reschedulePropertyRead(configData);
return -1;
}
- std::variant<bool> resp;
- reply.read(resp);
+
auto respValue = std::get_if<bool>(&resp);
if (!respValue)
{
@@ -2515,6 +2537,84 @@
}
return (*respValue);
}
+
+void setInitialValue(const ConfigData& configData, bool initialValue)
+{
+ if (configData.name == "PowerOk")
+ {
+ powerState = (initialValue ? PowerState::on : PowerState::off);
+ hostIface->set_property("CurrentHostState",
+ std::string(getHostState(powerState)));
+ }
+ else if (configData.name == "PowerButton")
+ {
+ powerButtonIface->set_property("ButtonPressed", !initialValue);
+ }
+ else if (configData.name == "ResetButton")
+ {
+ resetButtonIface->set_property("ButtonPressed", !initialValue);
+ }
+ else if (configData.name == "NMIButton")
+ {
+ nmiButtonIface->set_property("ButtonPressed", !initialValue);
+ }
+ else if (configData.name == "IdButton")
+ {
+ idButtonIface->set_property("ButtonPressed", !initialValue);
+ }
+ else if (configData.name == "PostComplete")
+ {
+ OperatingSystemStateStage osState =
+ (initialValue ? OperatingSystemStateStage::Inactive
+ : OperatingSystemStateStage::Standby);
+ setOperatingSystemState(osState);
+ }
+ else
+ {
+ lg2::error("Unknown name {NAME}", "NAME", configData.name);
+ }
+}
+
+void reschedulePropertyRead(const ConfigData& configData)
+{
+ auto item = dBusRetryTimers.find(configData.name);
+
+ if (item == dBusRetryTimers.end())
+ {
+ auto newItem = dBusRetryTimers.insert(
+ {configData.name, boost::asio::steady_timer(io)});
+
+ if (!newItem.second)
+ {
+ lg2::error("Failed to add new timer for {NAME}", "NAME",
+ configData.name);
+ return;
+ }
+
+ item = newItem.first;
+ }
+
+ auto& timer = item->second;
+ timer.expires_after(
+ std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
+ timer.async_wait([&configData](const boost::system::error_code ec) {
+ if (ec)
+ {
+ lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
+ configData.name, "MSG", ec.message());
+ dBusRetryTimers.erase(configData.name);
+ return;
+ }
+
+ int property = getProperty(configData);
+
+ if (property >= 0)
+ {
+ setInitialValue(configData, (property > 0));
+ dBusRetryTimers.erase(configData.name);
+ }
+ });
+}
} // namespace power_control
int main(int argc, char* argv[])