watchdog: Rewrite using sdbusplus
Change-Id: I730317954819859d23fdaca7336f19f5c5b0c107
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/Makefile.am b/Makefile.am
index d519ba7..2828e6f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -60,6 +60,7 @@
net.cpp \
app/channel.cpp \
app/watchdog.cpp \
+ app/watchdog_service.cpp \
apphandler.cpp \
sensorhandler.cpp \
storagehandler.cpp \
diff --git a/app/watchdog.cpp b/app/watchdog.cpp
index c8c9ced..d537683 100644
--- a/app/watchdog.cpp
+++ b/app/watchdog.cpp
@@ -1,25 +1,16 @@
#include "watchdog.hpp"
-#include "utils.hpp"
-#include <systemd/sd-bus.h>
+#include <cstdint>
+#include <endian.h>
+#include <phosphor-logging/log.hpp>
+#include <string>
-#include <mapper.h>
-#include <sdbusplus/bus.hpp>
+#include "watchdog_service.hpp"
+#include "host-ipmid/ipmid-api.h"
+#include "ipmid.hpp"
-extern sd_bus *bus;
-
-struct set_wd_data_t {
- uint8_t timer_use;
- uint8_t timer_action;
- uint8_t preset;
- uint8_t flags;
- uint8_t ls;
- uint8_t ms;
-} __attribute__ ((packed));
-
-static constexpr auto objname = "/xyz/openbmc_project/watchdog/host0";
-static constexpr auto iface = "xyz.openbmc_project.State.Watchdog";
-static constexpr auto property_iface = "org.freedesktop.DBus.Properties";
+using phosphor::logging::level;
+using phosphor::logging::log;
ipmi_ret_t ipmi_app_watchdog_reset(
ipmi_netfn_t netfn,
@@ -29,103 +20,56 @@
ipmi_data_len_t data_len,
ipmi_context_t context)
{
- sd_bus_message *reply = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
- int r = 0;
- char *busname = NULL;
-
- // Current properties of the watchdog daemon.
- int enabled = 0;
- uint64_t interval = 0;
-
- // Status code.
- ipmi_ret_t ret = IPMI_CC_UNSPECIFIED_ERROR;
+ // We never return data with this command so immediately get rid of it
*data_len = 0;
- printf("WATCHDOG RESET\n");
- // Get bus name
- r = mapper_get_service(bus, objname, &busname);
- if (r < 0) {
- fprintf(stderr, "Failed to get %s bus name: %s\n",
- objname, strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
+ try
+ {
+ WatchdogService wd_service;
+ WatchdogService::Properties wd_prop = wd_service.getProperties();
+
+ // Reset the countdown to make sure we don't expire our timer
+ wd_service.setTimeRemaining(wd_prop.interval);
+
+ // The spec states that the timer is activated by reset
+ wd_service.setEnabled(true);
+
+ return IPMI_CC_OK;
}
-
- // Check if our watchdog is running
- r = sd_bus_call_method(bus, busname, objname, property_iface,
- "Get", &error, &reply, "ss",
- iface, "Enabled");
- if(r < 0) {
- fprintf(stderr, "Failed to get current Enabled msg: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
+ catch (const std::exception& e)
+ {
+ const std::string e_str = std::string("wd_reset: ") + e.what();
+ log<level::ERR>(e_str.c_str());
+ return IPMI_CC_UNSPECIFIED_ERROR;
}
-
- // Now extract the value
- r = sd_bus_message_read(reply, "v", "b", &enabled);
- if (r < 0) {
- fprintf(stderr, "Failed to read current Enabled: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
+ catch (...)
+ {
+ log<level::ERR>("wd_reset: Unknown Error");
+ return IPMI_CC_UNSPECIFIED_ERROR;
}
-
- // If we are not enable we should indicate that
- if (!enabled) {
- printf("Watchdog not enabled during reset\n");
- ret = IPMI_WDOG_CC_NOT_INIT;
- goto finish;
- }
-
- sd_bus_error_free(&error);
- reply = sd_bus_message_unref(reply);
-
- // Get the current interval and set it back.
- r = sd_bus_call_method(bus, busname, objname, property_iface,
- "Get", &error, &reply, "ss",
- iface, "Interval");
-
- if(r < 0) {
- fprintf(stderr, "Failed to get current Interval msg: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
- }
-
- // Now extract the value
- r = sd_bus_message_read(reply, "v", "t", &interval);
- if (r < 0) {
- fprintf(stderr, "Failed to read current interval: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
- }
-
- sd_bus_error_free(&error);
- reply = sd_bus_message_unref(reply);
-
- // Set watchdog timer
- r = sd_bus_call_method(bus, busname, objname, property_iface,
- "Set", &error, &reply, "ssv",
- iface, "TimeRemaining", "t", interval);
- if(r < 0) {
- fprintf(stderr, "Failed to refresh the timer: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
- }
-
- ret = IPMI_CC_OK;
-finish:
- sd_bus_error_free(&error);
- reply = sd_bus_message_unref(reply);
- free(busname);
-
- return ret;
}
+static constexpr uint8_t wd_dont_stop = 0x1 << 6;
+static constexpr uint8_t wd_timeout_action_mask = 0x3;
+
+enum class IpmiAction : uint8_t {
+ None = 0x0,
+ HardReset = 0x1,
+ PowerOff = 0x2,
+ PowerCycle = 0x3,
+};
+
+struct wd_set_req {
+ uint8_t timer_use;
+ uint8_t timer_action;
+ uint8_t pretimeout; // (seconds)
+ uint8_t expire_flags;
+ uint16_t initial_countdown; // Little Endian (deciseconds)
+} __attribute__ ((packed));
+static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
+static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
+ "wd_get_res can't fit in request buffer.");
+
ipmi_ret_t ipmi_app_watchdog_set(
ipmi_netfn_t netfn,
ipmi_cmd_t cmd,
@@ -134,91 +78,57 @@
ipmi_data_len_t data_len,
ipmi_context_t context)
{
- sd_bus_message *reply = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
- int r = 0;
- ipmi_ret_t ret = IPMI_CC_UNSPECIFIED_ERROR;
-
- set_wd_data_t *reqptr = (set_wd_data_t*) request;
-
- uint16_t timer = 0;
-
- // Making this uint64_t to match with provider
- uint64_t timer_ms = 0;
- char *busname = NULL;
+ // Extract the request data
+ if (*data_len < sizeof(wd_set_req))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ wd_set_req req;
+ memcpy(&req, request, sizeof(req));
+ req.initial_countdown = le16toh(req.initial_countdown);
*data_len = 0;
- // Get number of 100ms intervals
- timer = (((uint16_t)reqptr->ms) << 8) + reqptr->ls;
- // Get timer value in ms
- timer_ms = timer * 100;
-
- printf("WATCHDOG SET Timer:[0x%X] 100ms intervals\n",timer);
-
- // Get bus name
- r = mapper_get_service(bus, objname, &busname);
- if (r < 0) {
- fprintf(stderr, "Failed to get %s bus name: %s\n",
- objname, strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
- }
-
- // Disable watchdog if running
- r = sd_bus_call_method(bus, busname, objname, property_iface,
- "Set", &error, &reply, "ssv",
- iface, "Enabled", "b", false);
- if(r < 0) {
- fprintf(stderr, "Failed to disable Watchdog: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
- }
-
- /*
- * If the action is 0, it means, do nothing. Multiple actions on timer
- * expiration aren't supported by phosphor-watchdog yet, so when the
- * action set is "none", we should just leave the timer disabled.
- */
- if (0 == reqptr->timer_action)
+ try
{
- ret = IPMI_CC_OK;
- goto finish;
- }
-
- if (reqptr->timer_use & 0x40)
- {
- sd_bus_error_free(&error);
- reply = sd_bus_message_unref(reply);
-
- // Set the Interval for the Watchdog
- r = sd_bus_call_method(bus, busname, objname, property_iface,
- "Set", &error, &reply, "ssv",
- iface, "Interval", "t", timer_ms);
- if(r < 0) {
- fprintf(stderr, "Failed to set new expiration time: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
+ WatchdogService wd_service;
+ // Stop the timer if the don't stop bit is not set
+ if (!(req.timer_use & wd_dont_stop))
+ {
+ wd_service.setEnabled(false);
}
- // Now Enable Watchdog
- r = sd_bus_call_method(bus, busname, objname, property_iface,
- "Set", &error, &reply, "ssv",
- iface, "Enabled", "b", true);
- if(r < 0) {
- fprintf(stderr, "Failed to Enable Watchdog: %s\n",
- strerror(-r));
- ret = IPMI_CC_BUSY;
- goto finish;
+ // Set the action based on the request
+ // Unfortunately we only really support enable or disable
+ // and don't actually support a real action. Until we have proper
+ // action support just map NONE as a disable action.
+ const auto ipmi_action = static_cast<IpmiAction>(
+ req.timer_action & wd_timeout_action_mask);
+ if (ipmi_action == IpmiAction::None)
+ {
+ wd_service.setEnabled(false);
}
+
+ // Set the new interval and the time remaining deci -> mill seconds
+ const uint64_t interval = req.initial_countdown * 100;
+ wd_service.setInterval(interval);
+ wd_service.setTimeRemaining(interval);
+
+ return IPMI_CC_OK;
}
-
- ret = IPMI_CC_OK;
-finish:
- sd_bus_error_free(&error);
- reply = sd_bus_message_unref(reply);
- free(busname);
-
- return ret;
+ catch (const std::domain_error &)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ catch (const std::exception& e)
+ {
+ const std::string e_str = std::string("wd_set: ") + e.what();
+ log<level::ERR>(e_str.c_str());
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ catch (...)
+ {
+ log<level::ERR>("wd_set: Unknown Error");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
}
diff --git a/app/watchdog_service.cpp b/app/watchdog_service.cpp
new file mode 100644
index 0000000..7f5d179
--- /dev/null
+++ b/app/watchdog_service.cpp
@@ -0,0 +1,69 @@
+#include "watchdog_service.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message.hpp>
+#include <string>
+
+#include "host-ipmid/ipmid-api.h"
+#include "utils.hpp"
+
+using sdbusplus::message::variant_ns::get;
+using sdbusplus::message::variant_ns::variant;
+
+static constexpr char wd_path[] = "/xyz/openbmc_project/watchdog/host0";
+static constexpr char wd_intf[] = "xyz.openbmc_project.State.Watchdog";
+static constexpr char prop_intf[] = "org.freedesktop.DBus.Properties";
+
+WatchdogService::WatchdogService()
+ : bus(ipmid_get_sd_bus_connection()),
+ wd_service(ipmi::getService(bus, wd_intf, wd_path))
+{
+}
+
+WatchdogService::Properties WatchdogService::getProperties()
+{
+ auto request = bus.new_method_call(wd_service.c_str(), wd_path,
+ prop_intf, "GetAll");
+ request.append(wd_intf);
+ auto response = bus.call(request);
+ if (response.is_method_error())
+ {
+ throw std::runtime_error("Failed to get watchdog properties");
+ }
+
+ std::map<std::string, variant<bool, uint64_t, std::string>> properties;
+ response.read(properties);
+ Properties wd_prop;
+ wd_prop.enabled = get<bool>(properties.at("Enabled"));
+ wd_prop.interval = get<uint64_t>(properties.at("Interval"));
+ wd_prop.timeRemaining = get<uint64_t>(properties.at("TimeRemaining"));
+ return wd_prop;
+}
+
+template <typename T>
+void WatchdogService::setProperty(const std::string& key, const T& val)
+{
+ auto request = bus.new_method_call(wd_service.c_str(), wd_path,
+ prop_intf, "Set");
+ request.append(wd_intf, key, variant<T>(val));
+ auto response = bus.call(request);
+ if (response.is_method_error())
+ {
+ throw std::runtime_error(std::string("Failed to set property: ") + key);
+ }
+}
+
+void WatchdogService::setEnabled(bool enabled)
+{
+ setProperty("Enabled", enabled);
+}
+
+void WatchdogService::setInterval(uint64_t interval)
+{
+ setProperty("Interval", interval);
+}
+
+void WatchdogService::setTimeRemaining(uint64_t timeRemaining)
+{
+ setProperty("TimeRemaining", timeRemaining);
+}
diff --git a/app/watchdog_service.hpp b/app/watchdog_service.hpp
new file mode 100644
index 0000000..4590e31
--- /dev/null
+++ b/app/watchdog_service.hpp
@@ -0,0 +1,61 @@
+#pragma once
+#include <sdbusplus/bus.hpp>
+
+/** @class WatchdogService
+ * @brief Access to the running OpenBMC watchdog implementation.
+ * @details Easy accessor for servers that implement the
+ * xyz.openbmc_project.State.Watchdog DBus API.
+ */
+class WatchdogService {
+ public:
+ WatchdogService();
+
+ /** @brief Contains a copy of the properties enumerated by the
+ * watchdog service.
+ */
+ struct Properties {
+ bool enabled;
+ uint64_t interval;
+ uint64_t timeRemaining;
+ };
+
+ /** @brief Retrieves a copy of the currently set properties on the
+ * host watchdog
+ *
+ * @return A populated WatchdogProperties struct
+ */
+ Properties getProperties();
+
+ /** @brief Sets the value of the enabled property on the host watchdog
+ *
+ * @param[in] enabled - The new enabled value
+ */
+ void setEnabled(bool enabled);
+
+ /** @brief Sets the value of the interval property on the host watchdog
+ *
+ * @param[in] interval - The new interval value
+ */
+ void setInterval(uint64_t interval);
+
+ /** @brief Sets the value of the timeRemaining property on the host
+ * watchdog
+ *
+ * @param[in] timeRemaining - The new timeRemaining value
+ */
+ void setTimeRemaining(uint64_t timeRemaining);
+
+ private:
+ /** @brief sdbusplus handle */
+ sdbusplus::bus::bus bus;
+ /** @brief The name of the mapped host watchdog service */
+ const std::string wd_service;
+
+ /** @brief Sets the value of the property on the host watchdog
+ *
+ * @param[in] key - The name of the property
+ * @param[in] val - The new value
+ */
+ template <typename T>
+ void setProperty(const std::string& key, const T& val);
+};