Synchronize NTP settings with systemd-timedated
Bmcweb get/set the "NTP settings" on phosphor-settings.
phosphor-time-manager and phosphor-settings are responsible to sync the
settings with systemd-timedated. So the services like bmcweb and ipmid
will only have to get/set the property on phosphor-settings.
However, in order to avoid a race condition issue with NTP set, bmcweb
now directly uses systemd instead of routing through phosphor-settings.
As a result, there may be differences in the "NTP settings" between
phosphor-setttings and systemd-timedated. To address this, this commit
adds a match to monitor time sync method changes on systemd-timedated
and updates it to phosphor-settings.
Tested:
```
1. Get current NTP setting:
busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP
b true
busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/time/sync_method xyz.openbmc_project.Time.Synchronization TimeSyncMethod -j
{
"type" : "s",
"data" : "xyz.openbmc_project.Time.Synchronization.Method.NTP"
}
2. Call method in systemd timedate, to change NTP setting:
busctl call org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 SetNTP bb false false
journal log:
phosphor-time-manager[28150]: Time mode has been changed to xyz.openbmc_project.Time.Synchronization.Method.Manual
phosphor-time-manager[28150]: NTP property changed in systemd time service, update to phosphor-settings.
phosphor-time-manager[28150]: NTP mode is already the same, skip setting to systemd time service again.
3. Get NTP setting again, NTP in phosphor-settings is synced with systemd-timedated:
busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP
b false
busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/time/sync_method xyz.openbmc_project.Time.Synchronization TimeSyncMethod -j
{
"type" : "s",
"data" : "xyz.openbmc_project.Time.Synchronization.Method.Manual"
}
4. Set property in phosphor-settings, to change NTP setting:
busctl set-property xyz.openbmc_project.Settings /xyz/openbmc_project/time/sync_method xyz.openbmc_project.Time.Synchronization TimeSyncMethod s "xyz.openbmc_project.Time.Synchronization.Method.NTP"
journal log:
phosphor-time-manager[28150]: Updated NTP setting: True
phosphor-time-manager[28150]: Time mode has been changed to xyz.openbmc_project.Time.Synchronization.Method.NTP
phosphor-time-manager[28150]: NTP property changed in phosphor-settings, update to systemd time service.
phosphor-time-manager[28150]: NTP mode is already the same, skip setting to phosphor-settings again.
5. Get NTP setting again, NTP in phosphor-settings is synced with systemd-timedated:
busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP
b true
busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/time/sync_method xyz.openbmc_project.Time.Synchronization TimeSyncMethod -j
{
"type" : "s",
"data" : "xyz.openbmc_project.Time.Synchronization.Method.NTP"
}
```
Signed-off-by: Jason Zhu <zhujiesen@bytedance.com>
Change-Id: I192d2257569f46aa0f5473331595c5242d816964
diff --git a/manager.cpp b/manager.cpp
index 1aeaf4b..c59c174 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -15,6 +15,7 @@
constexpr auto systemdTimePath = "/org/freedesktop/timedate1";
constexpr auto systemdTimeInterface = "org.freedesktop.timedate1";
constexpr auto methodSetNtp = "SetNTP";
+constexpr auto propertyNtp = "NTP";
} // namespace
namespace phosphor
@@ -27,6 +28,9 @@
Manager::Manager(sdbusplus::bus_t& bus) : bus(bus), settings(bus)
{
using namespace sdbusplus::bus::match::rules;
+ timedateMatches.emplace_back(
+ bus, propertiesChanged(systemdTimePath, systemdTimeInterface),
+ [&](sdbusplus::message_t& m) { onTimedateChanged(m); });
settingsMatches.emplace_back(
bus, propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf),
[&](sdbusplus::message_t& m) { onSettingsChanged(m); });
@@ -35,17 +39,29 @@
auto mode = getSetting(settings.timeSyncMethod.c_str(),
settings::timeSyncIntf, propertyTimeMode);
- onPropertyChanged(propertyTimeMode, mode);
+ onPropertyChanged(propertyTimeMode, mode, true);
}
void Manager::onPropertyChanged(const std::string& key,
- const std::string& value)
+ const std::string& value, bool forceSet)
{
assert(key == propertyTimeMode);
- // Notify listeners
- setCurrentTimeMode(value);
- onTimeModeChanged(value);
+ bool newNtpMode = (settings::ntpSync == value);
+ bool oldNtpMode = (Mode::NTP == getTimeMode());
+ if (forceSet || (newNtpMode != oldNtpMode))
+ {
+ // Notify listeners
+ onTimeModeChanged(value);
+ setCurrentTimeMode(value);
+ debug("NTP property changed in phosphor-settings, update to systemd"
+ " time service.");
+ }
+ else
+ {
+ debug("NTP mode is already the same, skip setting to systemd time"
+ " service again.");
+ }
}
int Manager::onSettingsChanged(sdbusplus::message_t& msg)
@@ -68,6 +84,52 @@
return 0;
}
+int Manager::onTimedateChanged(sdbusplus::message_t& msg)
+{
+ using Properties = std::map<std::string, std::variant<std::string, bool>>;
+
+ std::string interface;
+ Properties properties;
+
+ msg.read(interface, properties);
+
+ auto iter = properties.find(propertyNtp);
+ if (iter == properties.end())
+ {
+ return -1;
+ }
+
+ try
+ {
+ bool newNtpMode = std::get<bool>(iter->second);
+ bool oldNtpMode = (Mode::NTP == getTimeMode());
+ if (newNtpMode != oldNtpMode)
+ {
+ const auto& timeMode = newNtpMode ? settings::ntpSync
+ : settings::manualSync;
+ std::string settingManager = utils::getService(
+ bus, settings.timeSyncMethod.c_str(), settings::timeSyncIntf);
+ utils::setProperty(bus, settingManager, settings.timeSyncMethod,
+ settings::timeSyncIntf, propertyTimeMode,
+ timeMode);
+ setCurrentTimeMode(timeMode);
+ debug("NTP property changed in systemd time service, update to"
+ " phosphor-settings.");
+ }
+ else
+ {
+ debug("NTP mode is already the same, skip setting to"
+ " phosphor-settings again.");
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ error("Failed to sync NTP: {ERROR}", "ERROR", ex);
+ }
+
+ return 0;
+}
+
void Manager::updateNtpSetting(const std::string& value)
{
try
diff --git a/manager.hpp b/manager.hpp
index e8c28e9..263dbfb 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -48,6 +48,9 @@
/** @brief Persistent sdbusplus DBus connection */
sdbusplus::bus_t& bus;
+ /** @brief The match of systemd timedate property change */
+ std::vector<sdbusplus::bus::match_t> timedateMatches;
+
/** @brief The match of settings property change */
std::vector<sdbusplus::bus::match_t> settingsMatches;
@@ -85,6 +88,14 @@
*/
void onTimeModeChanged(const std::string& mode);
+ /** @brief Callback to handle change in NTP
+ *
+ * @param[in] msg - sdbusplus dbusmessage
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+ int onTimedateChanged(sdbusplus::message_t& msg);
+
/** @brief Callback to handle change in a setting
*
* @param[in] msg - sdbusplus dbusmessage
@@ -97,8 +108,12 @@
*
* @param[in] key - The name of property that is changed
* @param[in] value - The value of the property
+ * @param[in] forceSet - true: Force sync NTP Settings to systemd time
+ * service, only be used during initialization
+ * false: This is default value
*/
- void onPropertyChanged(const std::string& key, const std::string& value);
+ void onPropertyChanged(const std::string& key, const std::string& value,
+ bool forceSet = false);
/** @brief Update the NTP setting to systemd time service
*
diff --git a/settings.hpp b/settings.hpp
index 0753e1e..d7c986f 100644
--- a/settings.hpp
+++ b/settings.hpp
@@ -13,6 +13,9 @@
constexpr auto root = "/";
constexpr auto timeSyncIntf = "xyz.openbmc_project.Time.Synchronization";
+constexpr auto ntpSync = "xyz.openbmc_project.Time.Synchronization.Method.NTP";
+constexpr auto manualSync =
+ "xyz.openbmc_project.Time.Synchronization.Method.Manual";
/** @class Objects
* @brief Fetch paths of settings D-bus objects of interest upon construction
diff --git a/utils.hpp b/utils.hpp
index 34a67e8..eaf92c3 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -58,6 +58,42 @@
}
}
+/** @brief The template function to set property to the requested dbus path
+ *
+ * @param[in] bus - The Dbus bus object
+ * @param[in] service - The Dbus service name
+ * @param[in] path - The Dbus object path
+ * @param[in] interface - The Dbus interface
+ * @param[in] propertyName - The property name to set
+ * @param[in] value - the value to set the property to
+ *
+ */
+template <typename T>
+void setProperty(sdbusplus::bus_t& bus, const std::string& service,
+ const std::string& path, const std::string& interface,
+ const std::string& propertyName, T& value)
+{
+ std::variant<T> propertyValue(value);
+
+ auto method = bus.new_method_call(service.c_str(), path.c_str(),
+ "org.freedesktop.DBus.Properties", "Set");
+
+ method.append(interface, propertyName, propertyValue);
+
+ try
+ {
+ auto reply = bus.call(method);
+ }
+ catch (const sdbusplus::exception_t& ex)
+ {
+ error("SetProperty call failed, path:{PATH}, interface:{INTF}, "
+ "propertyName:{NAME}, error:{ERROR}",
+ "PATH", path, "INTF", interface, "NAME", propertyName, "ERROR",
+ ex);
+ throw std::runtime_error("SetProperty call failed");
+ }
+}
+
/** @brief Get service name from object path and interface
*
* @param[in] bus - The Dbus bus object