From 5257525db7dd97ffb8b5464df1b5b83df0297e7a Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" Date: Fri, 9 Feb 2018 15:54:56 -0800 Subject: watchdog: Rewrite using sdbusplus Change-Id: I730317954819859d23fdaca7336f19f5c5b0c107 Signed-off-by: William A. Kennington III --- app/watchdog.cpp | 270 ++++++++++++++++------------------------------- app/watchdog_service.cpp | 69 ++++++++++++ app/watchdog_service.hpp | 61 +++++++++++ 3 files changed, 220 insertions(+), 180 deletions(-) create mode 100644 app/watchdog_service.cpp create mode 100644 app/watchdog_service.hpp (limited to 'app') 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 +#include +#include +#include +#include -#include -#include +#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,102 +20,55 @@ ipmi_ret_t ipmi_app_watchdog_reset( 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(); - // 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; - } + // Reset the countdown to make sure we don't expire our timer + wd_service.setTimeRemaining(wd_prop.interval); - // 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; - } + // The spec states that the timer is activated by reset + wd_service.setEnabled(true); - // 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; + return IPMI_CC_OK; } - - 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; + catch (const std::exception& e) + { + const std::string e_str = std::string("wd_reset: ") + e.what(); + log(e_str.c_str()); + return IPMI_CC_UNSPECIFIED_ERROR; } - - // 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; + catch (...) + { + log("wd_reset: Unknown Error"); + return IPMI_CC_UNSPECIFIED_ERROR; } +} - 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; - } +static constexpr uint8_t wd_dont_stop = 0x1 << 6; +static constexpr uint8_t wd_timeout_action_mask = 0x3; - ret = IPMI_CC_OK; -finish: - sd_bus_error_free(&error); - reply = sd_bus_message_unref(reply); - free(busname); +enum class IpmiAction : uint8_t { + None = 0x0, + HardReset = 0x1, + PowerOff = 0x2, + PowerCycle = 0x3, +}; - return ret; -} +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, @@ -134,91 +78,57 @@ ipmi_ret_t ipmi_app_watchdog_set( 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; + try + { + 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); + } + + // 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( + req.timer_action & wd_timeout_action_mask); + if (ipmi_action == IpmiAction::None) + { + wd_service.setEnabled(false); + } - printf("WATCHDOG SET Timer:[0x%X] 100ms intervals\n",timer); + // 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); - // 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; + return IPMI_CC_OK; } - - // 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; + catch (const std::domain_error &) + { + return IPMI_CC_INVALID_FIELD_REQUEST; } - - /* - * 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) + catch (const std::exception& e) { - ret = IPMI_CC_OK; - goto finish; + const std::string e_str = std::string("wd_set: ") + e.what(); + log(e_str.c_str()); + return IPMI_CC_UNSPECIFIED_ERROR; } - - if (reqptr->timer_use & 0x40) + catch (...) { - 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; - } - - // 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; - } + log("wd_set: Unknown Error"); + return IPMI_CC_UNSPECIFIED_ERROR; } - - ret = IPMI_CC_OK; -finish: - sd_bus_error_free(&error); - reply = sd_bus_message_unref(reply); - free(busname); - - return ret; } 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 +#include +#include + +#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> properties; + response.read(properties); + Properties wd_prop; + wd_prop.enabled = get(properties.at("Enabled")); + wd_prop.interval = get(properties.at("Interval")); + wd_prop.timeRemaining = get(properties.at("TimeRemaining")); + return wd_prop; +} + +template +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(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 + +/** @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 + void setProperty(const std::string& key, const T& val); +}; -- cgit v1.2.1