summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishwanatha <vishwa@linux.vnet.ibm.com>2016-08-30 17:17:13 +0530
committerVishwanatha Subbanna <vishwa@linux.vnet.ibm.com>2016-11-09 14:22:59 +0530
commit81ee91f4507fe5a75df9dfa21fc406813a09a7ff (patch)
treea6f88287ba89fe74fafc784f74aeca8495efb0fd
parent008d34953af3a33f41dd009d71f2876946ef7e6f (diff)
downloadphosphor-time-manager-81ee91f4507fe5a75df9dfa21fc406813a09a7ff.tar.gz
phosphor-time-manager-81ee91f4507fe5a75df9dfa21fc406813a09a7ff.zip
Add TimeManager daemon to openbmc
Time Manager daemon supporting NTP and MANUAL modes and below policy *) BMC owns the time *) HOST owns the time *) SPLIT clock and HOST's time is maintained as an offset to BMC *) BOTH the BMC and HOST own the clock Change-Id: I81701b67731aa4b37d6926d5b93d397fea96e086 Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
-rw-r--r--Makefile.am11
-rwxr-xr-xbootstrap.sh16
-rw-r--r--configure.ac25
-rw-r--r--time-config.cpp590
-rw-r--r--time-config.hpp313
-rw-r--r--time-manager.cpp912
-rw-r--r--time-manager.hpp303
-rw-r--r--time-register.c25
-rw-r--r--time-register.hpp17
9 files changed, 2212 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..090ee2c
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,11 @@
+AM_DEFAULT_SOURCE_EXT = .cpp
+
+sbin_PROGRAMS = \
+ timemanager
+
+timemanager_SOURCES = \
+ time-register.c \
+ time-config.cpp \
+ time-manager.cpp
+
+timemanager_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS)
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..412550f
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile config.* \
+ configure depcomp install-sh ltmain.sh missing *libtool"
+
+case $1 in
+ clean)
+ test -f Makefile && make maintainer-clean
+ rm -rf ${AUTOCONF_FILES}
+
+ exit 0
+ ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f3f119b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,25 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([phosphor-time-manager], [1.0], [https://github.com/openbmc/phosphor-time-manager/issues])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+
+# Checks for programs.
+AC_PROG_CXX
+AX_CXX_COMPILE_STDCXX_14([noext])
+AC_PROG_CC
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for libraries.
+AC_CHECK_LIB([mapper], [mapper_get_service])
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+
+# Checks for header files.
+AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
+
+# Checks for typedefs, structures, and compiler characteristics.
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/time-config.cpp b/time-config.cpp
new file mode 100644
index 0000000..ff62782
--- /dev/null
+++ b/time-config.cpp
@@ -0,0 +1,590 @@
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <mapper.h>
+#include "time-manager.hpp"
+
+std::map<std::string, TimeConfig::FUNCTOR> TimeConfig::iv_TimeParams = {
+ { "time_mode", std::make_tuple(&TimeConfig::getSystemSettings,
+ &TimeConfig::updateTimeMode)
+ },
+
+ { "time_owner", std::make_tuple(&TimeConfig::getSystemSettings,
+ &TimeConfig::updateTimeOwner)
+ },
+
+ { "use_dhcp_ntp", std::make_tuple(&TimeConfig::getSystemSettings,
+ &TimeConfig::updateNetworkSettings)
+ }
+};
+
+TimeConfig::TimeConfig() :
+ iv_CurrTimeMode(timeModes::NTP),
+ iv_RequestedTimeMode(timeModes::NTP),
+ iv_CurrTimeOwner(timeOwners::BMC),
+ iv_RequestedTimeOwner(timeOwners::BMC),
+ iv_CurrDhcpNtp("yes"),
+ iv_SettingChangeAllowed(false),
+ iv_SplitModeChanged(false),
+ iv_dbus(nullptr)
+{
+ // Not really having anything to do here.
+}
+
+// Given a mode string, returns it's equivalent mode enum
+TimeConfig::timeModes TimeConfig::getTimeMode(const char* timeMode)
+{
+ // We are forcing the values to be in specific case and range
+ if (!strcmp(timeMode,"NTP"))
+ {
+ return timeModes::NTP;
+ }
+ else
+ {
+ return timeModes::MANUAL;
+ }
+}
+
+// Accepts a timeMode enum and returns it's string value
+const char* TimeConfig::modeStr(const TimeConfig::timeModes timeMode)
+{
+ switch(timeMode)
+ {
+ case timeModes::NTP:
+ {
+ return "NTP";
+ }
+ case timeModes::MANUAL:
+ {
+ return "MANUAL";
+ }
+ }
+}
+
+// Given a owner string, returns it's equivalent owner enum
+TimeConfig::timeOwners TimeConfig::getTimeOwner(const char* timeOwner)
+{
+ if (!strcmp(timeOwner,"BMC"))
+ {
+ return timeOwners::BMC;
+ }
+ else if (!strcmp(timeOwner,"HOST"))
+ {
+ return timeOwners::HOST;
+ }
+ else if (!strcmp(timeOwner,"SPLIT"))
+ {
+ return timeOwners::SPLIT;
+ }
+ else
+ {
+ return timeOwners::BOTH;
+ }
+}
+
+// Accepts a timeOwner enum and returns it's string value
+const char* TimeConfig::ownerStr(const timeOwners timeOwner)
+{
+ switch(timeOwner)
+ {
+ case timeOwners::BMC:
+ {
+ return "BMC";
+ }
+ case timeOwners::HOST:
+ {
+ return "HOST";
+ }
+ case timeOwners::SPLIT:
+ {
+ return "SPLIT";
+ }
+ case timeOwners::BOTH:
+ {
+ return "BOTH";
+ }
+ }
+}
+
+// Returns the busname that hosts objPath
+std::unique_ptr<char> TimeConfig::getProvider(const char* objPath)
+{
+ char *provider = nullptr;
+ mapper_get_service(iv_dbus, objPath, &provider);
+ return std::unique_ptr<char>(provider);
+}
+
+// Accepts a settings name and returns its value.
+// for the variant of type 'string' now.
+std::string TimeConfig::getSystemSettings(const char* key)
+{
+ constexpr auto settingsObj = "/org/openbmc/settings/host0";
+ constexpr auto propertyIntf = "org.freedesktop.DBus.Properties";
+ constexpr auto hostIntf = "org.openbmc.settings.Host";
+
+ const char* value = nullptr;
+ std::string settingsVal {};
+ sd_bus_message* reply = nullptr;
+
+ std::cout <<"Getting System Settings: " << key << std::endl;
+
+ // Get the provider from object mapper
+ auto settingsProvider = getProvider(settingsObj);
+ if (!settingsProvider)
+ {
+ std::cerr << "Error Getting service for Settings" << std::endl;
+ return value;
+ }
+
+ auto r = sd_bus_call_method(iv_dbus,
+ settingsProvider.get(),
+ settingsObj,
+ propertyIntf,
+ "Get",
+ nullptr,
+ &reply,
+ "ss",
+ hostIntf,
+ key);
+ if (r < 0)
+ {
+ std::cerr <<"Error" << strerror(-r)
+ <<" reading system settings" << std::endl;
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "v", "s", &value);
+ if (r < 0)
+ {
+ std::cerr <<"Error " << strerror(-r)
+ <<" parsing settings data" << std::endl;
+ }
+finish:
+ if (value)
+ {
+ settingsVal.assign(value);
+ }
+ reply = sd_bus_message_unref(reply);
+ return settingsVal;
+}
+
+// Reads value from /org/openbmc/control/power0
+// This signature on purpose to plug into time parameter map
+std::string TimeConfig::getPowerSetting(const char* key)
+{
+ constexpr auto powerObj = "/org/openbmc/control/power0";
+ constexpr auto powerIntf = "org.openbmc.control.Power";
+ constexpr auto propertyIntf = "org.freedesktop.DBus.Properties";
+
+ int value = -1;
+ std::string powerValue {};
+ sd_bus_message* reply = nullptr;
+
+ std::cout <<"Reading Power Control key: " << key << std::endl;
+
+ // Get the provider from object mapper
+ auto powerProvider = getProvider(powerObj);
+ if (!powerProvider)
+ {
+ std::cerr <<" Error getting provider for Power Settings" << std::endl;
+ return powerValue;
+ }
+
+ auto r = sd_bus_call_method(iv_dbus,
+ powerProvider.get(),
+ powerObj,
+ propertyIntf,
+ "Get",
+ nullptr,
+ &reply,
+ "ss",
+ powerIntf,
+ key);
+ if (r < 0)
+ {
+ std::cerr <<"Error " << strerror(-r)
+ << "reading: " << key << std::endl;
+ goto finish;
+ }
+
+ r = sd_bus_message_read(reply, "v", "i", &value);
+ if (r < 0)
+ {
+ std::cerr <<"Error " << strerror(-r)
+ <<" parsing " << key << "value" << std::endl;
+ // For maintenance
+ goto finish;
+ }
+finish:
+ if (value != -1)
+ {
+ powerValue = std::to_string(value);
+ }
+ reply = sd_bus_message_unref(reply);
+ return powerValue;
+}
+
+// Updates .network file with UseNtp=
+int TimeConfig::updateNetworkSettings(const std::string& useDhcpNtp)
+{
+ constexpr auto networkObj = "/org/openbmc/NetworkManager/Interface";
+ constexpr auto networkIntf = "org.openbmc.NetworkManager";
+
+ std::cout << "use_dhcp_ntp = " << useDhcpNtp.c_str() << std::endl;
+
+ // If what we have already is what it is, then just return.
+ if (iv_CurrDhcpNtp == useDhcpNtp)
+ {
+ return 0;
+ }
+
+ // Get the provider from object mapper
+ auto networkProvider = getProvider(networkObj);
+ if (!networkProvider)
+ {
+ return -1;
+ }
+
+ auto r = sd_bus_call_method(iv_dbus,
+ networkProvider.get(),
+ networkObj,
+ networkIntf,
+ "UpdateUseNtpField",
+ nullptr,
+ nullptr,
+ "s",
+ useDhcpNtp.c_str());
+ if (r < 0)
+ {
+ std::cerr <<"Error " << strerror(-r)
+ << " updating UseNtp" << std::endl;
+ }
+ else
+ {
+ std::cout <<"Successfully updated UseNtp=["
+ << useDhcpNtp << "]" << std::endl;
+
+ r = writeData<decltype(useDhcpNtp)>(cv_DhcpNtpFile, useDhcpNtp);
+ }
+
+ return 0;
+}
+
+// Reads the values from 'settingsd' and applies:
+// 1) Time Mode
+// 2) time Owner
+// 3) UseNTP setting
+// 4) Pgood
+int TimeConfig::processInitialSettings(sd_bus* dbus)
+{
+ // First call from TimeManager to config manager
+ iv_dbus = dbus;
+
+ // Read saved info like Who was the owner , what was the mode,
+ // what was the use_dhcp_ntp setting before etc..
+ auto r = readPersistentData();
+ if (r < 0)
+ {
+ std::cerr << "Error reading the data saved in flash."
+ << std::endl;
+ return r;
+ }
+
+ // Now read whats in settings and apply if allowed.
+ for (auto& iter : iv_TimeParams)
+ {
+ // Get the settings value for various keys.
+ auto reader = std::get<READER>(iter.second);
+ auto value = (this->*reader)(iter.first.c_str());
+ if (!value.empty())
+ {
+ // Get the value for the key and validate.
+ auto updater = std::get<UPDATER>(iter.second);
+ auto r = (this->*updater)(value);
+ if (r < 0)
+ {
+ std::cerr << "Error setting up initial keys" << std::endl;
+ return r;
+ }
+ }
+ else
+ {
+ std::cerr << "key " << iter.first
+ <<" has no value: " << std::endl;
+ return -1;
+ }
+ }
+
+ // Now that we have taken care of consuming, push this as well
+ // so that we can use the same map for handling pgood change too.
+ auto readerUpdater = std::make_tuple(&TimeConfig::getPowerSetting,
+ &TimeConfig::processPgoodChange);
+ iv_TimeParams.emplace("pgood", readerUpdater);
+
+ return 0;
+}
+
+// This is called by Property Change handler on the event of
+// receiving notification on property value change.
+int TimeConfig::updatePropertyVal(const char* key, const std::string& value)
+{
+ auto iter = iv_TimeParams.find(key);
+ if (iter != iv_TimeParams.end())
+ {
+ auto updater = std::get<UPDATER>(iter->second);
+ return (this->*updater)(value);
+ }
+ // Coming here indicates that we never had a matching key.
+ return -1;
+}
+
+// Called by sd_event when Properties are changed in Control/power0
+// Interested in change to 'pgood'
+int TimeConfig::processPgoodChange(const std::string& newPgood)
+{
+ // Indicating that we are safe to apply any changes
+ if (!newPgood.compare("0"))
+ {
+ iv_SettingChangeAllowed = true;
+ std::cout <<"Changing time settings allowed now" << std::endl;
+ }
+ else
+ {
+ iv_SettingChangeAllowed = false;
+ std::cout <<"Changing time settings is *deferred* now" << std::endl;
+ }
+
+ // if we have had users that changed the time settings
+ // when we were not ready yet, do it now.
+ if (iv_RequestedTimeOwner != iv_CurrTimeOwner)
+ {
+ auto r = updateTimeOwner(ownerStr(iv_RequestedTimeOwner));
+ if (r < 0)
+ {
+ std::cerr << "Error updating new time owner" << std::endl;
+ return r;
+ }
+ std::cout << "New Owner is : "
+ << ownerStr(iv_RequestedTimeOwner) << std::endl;
+ }
+
+ if (iv_RequestedTimeMode != iv_CurrTimeMode)
+ {
+ auto r = updateTimeMode(modeStr(iv_RequestedTimeMode));
+ if (r < 0)
+ {
+ std::cerr << "Error updating new time mode" << std::endl;
+ return r;
+ }
+ std::cout <<"New Mode is : "
+ << modeStr(iv_RequestedTimeMode) << std::endl;
+ }
+ return 0;
+}
+
+// Manipulates time owner if the system setting allows it
+int TimeConfig::updateTimeMode(const std::string& newModeStr)
+{
+ auto r = 0;
+ iv_RequestedTimeMode = getTimeMode(newModeStr.c_str());
+
+ std::cout <<"Requested_Mode: " << newModeStr
+ << " Current_Mode: " << modeStr(iv_CurrTimeMode)
+ << std::endl;
+
+ if (iv_RequestedTimeMode == iv_CurrTimeMode)
+ {
+ std::cout << "Mode is already set to : "
+ << newModeStr << std::endl;
+ return 0;
+ }
+
+ // Also, if the time owner is HOST, then we should not allow NTP.
+ // However, it may so happen that there are 2 pending requests, one for
+ // changing to NTP and other for changing owner to something not HOST.
+ // So check if there is a pending timeOwner change and if so, allow NTP
+ // if the current is HOST and requested is non HOST.
+ if (iv_CurrTimeOwner == timeOwners::HOST &&
+ iv_RequestedTimeOwner == timeOwners::HOST &&
+ iv_RequestedTimeMode == timeModes::NTP)
+ {
+ std::cout <<"Can not set mode to NTP with HOST as owner"
+ << std::endl;
+ return 0;
+ }
+
+ if (iv_SettingChangeAllowed)
+ {
+ r = modifyNtpSettings(iv_RequestedTimeMode);
+ if (r < 0)
+ {
+ std::cerr << "Error changing TimeMode settings"
+ << std::endl;
+ }
+ else
+ {
+ iv_CurrTimeMode = iv_RequestedTimeMode;
+ }
+ std::cout << "Current_Mode changed to: "
+ << newModeStr << " :: " << modeStr(iv_CurrTimeMode) << std::endl;
+
+ // Need this when we either restart or come back from reset
+ r = writeData(cv_TimeModeFile, modeStr(iv_CurrTimeMode));
+ }
+ else
+ {
+ std::cout <<"Deferring update until system state allows it"
+ << std::endl;
+ }
+ return r;
+}
+
+// Manipulates time owner if the system setting allows it
+int TimeConfig::updateTimeOwner(const std::string& newOwnerStr)
+{
+ int r = 0;
+ iv_RequestedTimeOwner = getTimeOwner(newOwnerStr.c_str());
+
+ // Needed when owner changes to HOST
+ std::string manualMode = "Manual";
+
+ // Needed by time manager to do some house keeping
+ iv_SplitModeChanged = false;
+
+ if (iv_RequestedTimeOwner == iv_CurrTimeOwner)
+ {
+ std::cout <<"Owner is already set to : "
+ << newOwnerStr << std::endl;
+ return 0;
+ }
+
+ std::cout <<"Requested_Owner: " << newOwnerStr
+ << " Current_Owner: " << ownerStr(iv_CurrTimeOwner)
+ << std::endl;
+
+ if (iv_SettingChangeAllowed)
+ {
+ // If we are transitioning from SPLIT to something else,
+ // reset the host offset.
+ if (iv_CurrTimeOwner == timeOwners::SPLIT &&
+ iv_RequestedTimeOwner != timeOwners::SPLIT)
+ {
+ // Needed by time manager to do some house keeping
+ iv_SplitModeChanged = true;
+ }
+ iv_CurrTimeOwner = iv_RequestedTimeOwner;
+ std::cout << "Current_Owner is now: "
+ << newOwnerStr << std::endl;
+
+ // HOST and NTP are exclusive
+ if (iv_CurrTimeOwner == timeOwners::HOST)
+ {
+ std::cout <<"Forcing the mode to MANUAL" << std::endl;
+ r = updateTimeMode(manualMode);
+ if (r < 0)
+ {
+ std::cerr << "Error forcing the mode to MANUAL" << std::endl;
+ return r;
+ }
+ }
+ // Need this when we either restart or come back from reset
+ r = writeData(cv_TimeOwnerFile, ownerStr(iv_CurrTimeOwner));
+ }
+ else
+ {
+ std::cout <<"Deferring update until system state allows it"
+ << std::endl;
+ }
+
+ return r;
+}
+
+// Accepts the time mode and makes necessary changes to timedate1
+int TimeConfig::modifyNtpSettings(const timeModes& newTimeMode)
+{
+ auto ntpChangeOp = 0;
+
+ // Pass '1' -or- '0' to SetNTP method indicating Enable/Disable
+ ntpChangeOp = (newTimeMode == timeModes::NTP) ? 1 : 0;
+
+ std::cout <<"Applying NTP setting..." << std::endl;
+
+ auto r = sd_bus_call_method(iv_dbus,
+ "org.freedesktop.timedate1",
+ "/org/freedesktop/timedate1",
+ "org.freedesktop.timedate1",
+ "SetNTP",
+ nullptr,
+ nullptr, // timedate1 does not return response
+ "bb",
+ ntpChangeOp, // '1' means Enable
+ 0); // '0' meaning no policy-kit
+ if (r < 0)
+ {
+ std::cerr <<"Error: " << strerror(-r)
+ << "changing time Mode" << std::endl;
+ }
+ else
+ {
+ std::cout << "SUCCESS. NTP setting is now: " <<
+ (ntpChangeOp) ? "Enabled" : "Disabled";
+
+ // TODO : https://github.com/openbmc/phosphor-time-manager/issues/1
+ if (ntpChangeOp)
+ {
+ system("systemctl restart systemd-timesyncd &");
+ }
+ else
+ {
+ system("systemctl stop systemd-timesyncd &");
+ }
+ }
+ return r;
+}
+
+// Reads all the saved data from the last run
+int TimeConfig::readPersistentData()
+{
+ // If we are coming back from a reset reload, then need to
+ // read what was the last successful Mode and Owner.
+ auto savedTimeMode = readData<std::string>(cv_TimeModeFile);
+ if (!savedTimeMode.empty())
+ {
+ iv_CurrTimeMode = getTimeMode(savedTimeMode.c_str());
+ std::cout <<"Last known time_mode: "
+ << savedTimeMode.c_str() << std::endl;
+ }
+
+ auto savedTimeOwner = readData<std::string>(cv_TimeOwnerFile);
+ if (!savedTimeOwner.empty())
+ {
+ iv_CurrTimeOwner = getTimeOwner(savedTimeOwner.c_str());
+ std::cout <<"Last known time_owner: "
+ << savedTimeOwner.c_str() << std::endl;
+ }
+
+ auto savedDhcpNtp = readData<std::string>(cv_DhcpNtpFile);
+ if (!savedDhcpNtp.empty())
+ {
+ iv_CurrDhcpNtp = savedDhcpNtp;
+ std::cout <<"Last known use_dhcp_ntp: "
+ << iv_CurrDhcpNtp.c_str() << std::endl;
+ }
+ else
+ {
+ // This seems to be the first time.
+ std::cerr <<"Empty DhcpNtp string" << std::endl;
+ iv_CurrDhcpNtp = "yes";
+ }
+
+ // Doing this here to make sure 'pgood' is read and handled
+ // first before anything.
+ auto pgood = getPowerSetting("pgood");
+ if (!pgood.compare("0"))
+ {
+ std::cout << "Changing settings *allowed* now" << std::endl;
+ iv_SettingChangeAllowed = true;
+ }
+ return 0;
+}
diff --git a/time-config.hpp b/time-config.hpp
new file mode 100644
index 0000000..0f08641
--- /dev/null
+++ b/time-config.hpp
@@ -0,0 +1,313 @@
+#include <map>
+#include <systemd/sd-bus.h>
+
+/** @class TimeConfig
+ * @brief Maintains various time modes and time owners.
+ */
+class TimeConfig
+{
+public:
+ /** @brief Supported time modes
+ * NTP Time sourced by Network Time Server
+ * MANUAL User of the system need to set the time
+ */
+ enum class timeModes
+ {
+ NTP,
+ MANUAL
+ };
+
+ /** @brief Supported time owners
+ * BMC Time source may be NTP or MANUAL but it has to be set natively
+ * on the BMC. Meaning, host can not set the time. What it also
+ * means is that when BMC gets IPMI_SET_SEL_TIME, then its ignored.
+ * similarly, when BMC gets IPMI_GET_SEL_TIME, then the BMC's time
+ * is returned.
+ *
+ * HOST Its only IPMI_SEL_SEL_TIME that will set the time on BMC.
+ * Meaning, IPMI_GET_SEL_TIME and request to get BMC time will
+ * result in same value.
+ *
+ * SPLIT Both BMC and HOST will maintain their individual clocks but then
+ * the time information is stored in BMC. BMC can have either NTP
+ * or MANUAL as it's source of time and will set the time directly
+ * on the BMC. When IPMI_SET_SEL_TIME is received, then the delta
+ * between that and BMC's time is calculated and is stored.
+ * When BMC reads the time, the current time is returned.
+ * When IPMI_GET_SEL_TIME is received, BMC's time is retrieved and
+ * then the delta offset is factored in prior to returning.
+ *
+ * BOTH: BMC's time is set with whoever that sets the time. Similarly,
+ * BMC's time is returned to whoever that asks the time.
+ */
+ enum class timeOwners
+ {
+ BMC,
+ HOST,
+ SPLIT,
+ BOTH
+ };
+
+ // Do not have a usecase of copying this object so disable
+ TimeConfig();
+ ~TimeConfig() = default;
+ TimeConfig(const TimeConfig&) = delete;
+ TimeConfig& operator=(const TimeConfig&) = delete;
+ TimeConfig(TimeConfig&&) = delete;
+ TimeConfig& operator=(TimeConfig&&) = delete;
+
+ inline auto getCurrTimeMode() const
+ {
+ return iv_CurrTimeMode;
+ }
+
+ inline auto getCurrTimeOwner() const
+ {
+ return iv_CurrTimeOwner;
+ }
+
+ inline auto getRequestedTimeMode() const
+ {
+ return iv_RequestedTimeMode;
+ }
+
+ inline auto getRequestedTimeOwner() const
+ {
+ return iv_RequestedTimeOwner;
+ }
+
+ inline auto isSplitModeChanged() const
+ {
+ return iv_SplitModeChanged;
+ }
+
+ inline void updateSplitModeFlag(const bool& value)
+ {
+ iv_SplitModeChanged = value;
+ }
+
+ inline sd_bus* getDbus() const
+ {
+ return iv_dbus;
+ }
+
+ /** brief Generic file reader used to read time mode,
+ * time owner, host time, host offset and useDhcpNtp
+ *
+ * @param[in] filename - Name of file where data is preserved.
+ * @return - File content
+ */
+ template <typename T>
+ T readData(const char* fileName)
+ {
+ T data = T();
+ if(std::ifstream(fileName))
+ {
+ std::ifstream file(fileName, std::ios::in);
+ file >> data;
+ file.close();
+ }
+ return data;
+ }
+
+ /** @brief Generic file writer used to write time mode,
+ * time owner, host time, host offset and useDhcpNtp
+ *
+ * @param[in] filename - Name of file where data is preserved.
+ * @param[in] data - Data to be written to file
+ * @return - 0 for now. But will be changed to raising
+ * - Exception
+ */
+ template <typename T>
+ auto writeData(const char* fileName, const T&& data)
+ {
+ std::ofstream file(fileName, std::ios::out);
+ file << data;
+ file.close();
+ return 0;
+ }
+
+ /** @brief Reads saved data and populates below properties
+ * - Current Time Mode
+ * - Current Time Owner
+ * - Whether to use NTP settings given by DHCP
+ * - Last known host offset
+ *
+ * @param[in] dbus - Handler to sd_bus used by time manager
+ *
+ * @return - < 0 for failure and others for success
+ */
+ int processInitialSettings(sd_bus* dbus);
+
+ /** @brief Accepts time mode string, returns the equivalent enum
+ *
+ * @param[in] timeModeStr - Current Time Mode in string
+ *
+ * @return - Equivalent ENUM
+ * - Input : "NTP", Output: timeModes::NTP
+ */
+ static timeModes getTimeMode(const char* timeModeStr);
+
+ /** @brief Accepts timeMode enum and returns it's string equivalent
+ *
+ * @param[in] timeMode - Current Time Mode Enum
+ *
+ * @return - Equivalent string
+ * - Input : timeModes::NTP, Output : "NTP"
+ */
+ static const char* modeStr(const timeModes timeMode);
+
+ /** @brief Accepts timeOwner string and returns it's equivalent enum
+ *
+ * @param[in] timeOwnerStr - Current Time Owner in string
+ *
+ * @return - Equivalent ENUM
+ * - Input : "BMC", output : timeOwners::BMC
+ */
+ static timeOwners getTimeOwner(const char* timeOwnerStr);
+
+ /** @brief Accepts timeOwner enum and returns it's string equivalent
+ *
+ * @param[in] timeOwner - Current Time Mode Enum
+ *
+ * @return - Equivalent string
+ * - Input : timeOwners::BMC, Output : "BMC"
+ */
+ static const char* ownerStr(const timeOwners timeOwner);
+
+ /** @brief Gets called when the settings property changes.
+ * Walks the map and then applies the changes
+ *
+ * @param[in] key - Name of the property
+ * @param[in] value - Value
+ *
+ * @return - < 0 on failure, success otherwise
+ */
+ int updatePropertyVal(const char* key, const std::string& value);
+
+ // Acts on the time property changes / reads initial property
+ using READER = std::string (TimeConfig::*) (const char*);
+ using UPDATER = int (TimeConfig::*) (const std::string&);
+ using FUNCTOR = std::tuple<READER, UPDATER>;
+
+ // Most of this is statically constructed and PGOOD is added later.
+ static std::map<std::string, FUNCTOR> iv_TimeParams;
+
+private:
+ // Bus initialised by manager on a call to process initial settings
+ sd_bus *iv_dbus;
+
+ /** @brief 'Requested' is what is asked by user in settings
+ * 'Current' is what the TimeManager is really using since its not
+ * possible to apply the mode and owner as and when the user updates
+ * Settings. They are only applied when the system power is off.
+ */
+ timeModes iv_CurrTimeMode;
+ timeModes iv_RequestedTimeMode;
+
+ timeOwners iv_CurrTimeOwner;
+ timeOwners iv_RequestedTimeOwner;
+
+ /** @brief One of the entry in .network file indicates whether the
+ * systemd-timesyncd should use the NTP server list that are sent by DHCP
+ * server or not. If the value is 'yes', then NTP server list sent by DHCP
+ * are used. else entries that are configured statically with NTP= are used
+ */
+ std::string iv_CurrDhcpNtp;
+
+ /** @brief Dictated by state of pgood. When the pgood value is 'zero', then
+ * its an indication that we are okay to apply any pending Mode / Owner
+ * values. Meaning, Current will be updated with what is in Requested.
+ */
+ bool iv_SettingChangeAllowed;
+
+ // Needed to nudge Time Manager to reset offset
+ bool iv_SplitModeChanged;
+
+ static constexpr auto cv_TimeModeFile = "/var/lib/obmc/saved_timeMode";
+ static constexpr auto cv_TimeOwnerFile = "/var/lib/obmc/saved_timeOwner";
+ static constexpr auto cv_DhcpNtpFile = "/var/lib/obmc/saved_dhcpNtp";
+
+ /** @brief Wrapper that looks up in the mapper service for a given
+ * object path and returns the service name
+ *
+ * @param[in] objpath - dbus object path
+ *
+ * @return - unique_ptr holding service name.
+ * - Caller needs to validate if its populated
+ */
+ std::unique_ptr<char> getProvider(const char* objPath);
+
+ /** @brief Helper function for processInitialSettings.
+ * Reads saved data and populates below properties. Only the values are
+ * read from the file system. but it will not take the action on those.
+ * Actions on these are taken by processInitialSettings
+ *
+ * - Current Time Mode
+ * - Current Time Owner
+ * - Whether to use NTP settings given by DHCP
+ * - Current Pgood state
+ *
+ * @return - < 0 for failure and success on others
+ */
+ int readPersistentData();
+
+ /** @brief Updates the 'ntp' field in systemd/timedate1,
+ * which enables / disables NTP syncing
+ *
+ * @param[in] newTimeMode - Time Mode Enum
+ *
+ * @return - < 0 on failure and success on others
+ */
+ int modifyNtpSettings(const timeModes& newTimeMode);
+
+ /** @brief Accepts system setting parameter and returns its value
+ *
+ * @param[in] key - Name of the property
+ *
+ * @return - Value as string
+ */
+ std::string getSystemSettings(const char* key);
+
+ /** @brief Reads the data hosted by /org/openbmc/control/power0
+ *
+ * @param[in] key - Name of the property
+ *
+ * @return - Value as string
+ */
+ std::string getPowerSetting(const char* key);
+
+ /** @brief Accepts Mode string and applies only if conditions allow it.
+ *
+ * @param[in] newModeStr - Requested Time Mode
+ *
+ * @return - < 0 on failure, success otherwise
+ */
+ int updateTimeMode(const std::string& newModeStr);
+
+ /** @brief Accepts Ownere string and applies only if conditions allow it.
+ *
+ * @param[in] newOwnerStr - Requested Time Owner
+ *
+ * @return - < 0 on failure, success otherwise
+ */
+
+ int updateTimeOwner(const std::string& newownerStr);
+
+ /** @brief Updates .network file with UseNtp= provided by NetworkManager
+ *
+ * @param[in] useDhcpNtp - will be 'yes' or 'no'
+ *
+ * @return - < 0 on failure, success otherwise
+ */
+ int updateNetworkSettings(const std::string& useDhcpNtp);
+
+ /** @brief Accepts current pgood value and then updates any pending mode
+ * or owner requests
+ *
+ * @param[in] useDhcpNtp - will be 'yes' or 'no'
+ *
+ * @return - < 0 on failure, success otherwise
+ */
+ int processPgoodChange(const std::string& newPgood);
+};
diff --git a/time-manager.cpp b/time-manager.cpp
new file mode 100644
index 0000000..bde2e0d
--- /dev/null
+++ b/time-manager.cpp
@@ -0,0 +1,912 @@
+#define _XOPEN_SOURCE
+#include <chrono>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
+#include <array>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/timerfd.h>
+#include <systemd/sd-event.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-daemon.h>
+#include "time-register.hpp"
+#include "time-manager.hpp"
+
+// Neeed to do this since its not exported outside of the kernel.
+// Refer : https://gist.github.com/lethean/446cea944b7441228298
+#ifndef TFD_TIMER_CANCEL_ON_SET
+#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#endif
+
+// Needed to make sure timerfd does not misfire eventhough we set CANCEL_ON_SET
+#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
+
+// Used in time-register.c
+extern sd_bus_vtable timeServicesVtable[];
+
+Time::Time(const TimeConfig& timeConfig) : config(timeConfig)
+{
+ // Nothing to do here
+}
+
+BmcTime::BmcTime(const TimeConfig& timeConfig) : Time(timeConfig)
+{
+ // Nothing to do here
+}
+
+HostTime::HostTime(const TimeConfig& timeConfig,
+ const std::chrono::microseconds& hostOffset)
+ : Time(timeConfig),
+ iv_Offset(hostOffset)
+{
+ // Nothing to do here
+}
+
+TimeManager::TimeManager() :
+ iv_HostOffset(std::chrono::microseconds(0)),
+ iv_UptimeUsec(std::chrono::microseconds(0)),
+ iv_EventSource(nullptr),
+ iv_Event(nullptr)
+{
+ assert(setupTimeManager() >= 0);
+}
+
+// Needed to be standalone extern "C" to register
+// as a callback routine with sd_bus_vtable
+int GetTime(sd_bus_message* m, void* userdata,
+ sd_bus_error* retError)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ return tmgr->getTime(m, userdata, retError);
+}
+
+int SetTime(sd_bus_message* m, void* userdata,
+ sd_bus_error* retError)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ return tmgr->setTime(m, userdata, retError);
+}
+
+// Property reader
+int getCurrTimeModeProperty(sd_bus* bus, const char* path,
+ const char* interface, const char* property,
+ sd_bus_message* m, void* userdata,
+ sd_bus_error* error)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ return sd_bus_message_append(m, "s",
+ TimeConfig::modeStr(tmgr->config.getCurrTimeMode()));
+}
+
+int getCurrTimeOwnerProperty(sd_bus* bus, const char* path,
+ const char* interface, const char* property,
+ sd_bus_message* m, void* userdata,
+ sd_bus_error* error)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ return sd_bus_message_append(m, "s",
+ TimeConfig::ownerStr(tmgr->config.getCurrTimeOwner()));
+}
+
+int getReqTimeModeProperty(sd_bus* bus, const char* path,
+ const char* interface, const char* property,
+ sd_bus_message* m, void* userdata,
+ sd_bus_error* error)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ return sd_bus_message_append(m, "s",
+ TimeConfig::modeStr(tmgr->config.getRequestedTimeMode()));
+}
+
+int getReqTimeOwnerProperty(sd_bus* bus, const char* path,
+ const char* interface, const char* property,
+ sd_bus_message* m, void* userdata,
+ sd_bus_error* error)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ return sd_bus_message_append(m, "s",
+ TimeConfig::ownerStr(tmgr->config.getRequestedTimeOwner()));
+}
+
+int TimeManager::getTime(sd_bus_message* m, void* userdata,
+ sd_bus_error* retError)
+{
+ const char* target = nullptr;
+
+ // Extract the target and call respective GetTime
+ auto r = sd_bus_message_read(m, "s", &target);
+ if (r < 0)
+ {
+ std::cerr << "Error:" << strerror(-r)
+ <<" reading user time" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_IO_ERROR, "Error reading input");
+
+ return sd_bus_reply_method_error(m, retError);
+ }
+
+ if (!strcasecmp(target, "bmc"))
+ {
+ auto time = BmcTime(config);
+ return time.getTime(m, retError);
+ }
+ else if (!strcasecmp(target, "host"))
+ {
+ auto time = HostTime(config, iv_HostOffset);
+ return time.getTime(m, retError);
+ }
+ else
+ {
+ std::cerr << "Error:" << strerror(-r)
+ <<" Invalid Target" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_IO_ERROR, "Valid targets are BMC or HOST");
+
+ return sd_bus_reply_method_error(m, retError);
+ }
+}
+
+int TimeManager::setTime(sd_bus_message* m, void* userdata,
+ sd_bus_error* retError)
+{
+ long long int timeInUsec {};
+
+ const char* target = nullptr;
+ auto r = sd_bus_message_read(m, "s", &target);
+ if (r < 0)
+ {
+ std::cerr << "Error:" << strerror(-r)
+ <<" reading user time" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_IO_ERROR, "Error reading input");
+
+ return sd_bus_reply_method_error(m, retError);
+ }
+
+ if (!strcasecmp(target, "bmc"))
+ {
+ auto time = BmcTime(config);
+ auto r = time.setTime(m, retError);
+ if (r < 0)
+ {
+ // This would have the error populated
+ return sd_bus_reply_method_error(m, retError);
+ }
+ }
+ else if (!strcasecmp(target, "host"))
+ {
+ auto time = HostTime(config, iv_HostOffset);
+
+ auto r = time.setTime(m, retError);
+ if (r < 0)
+ {
+ // This would have the error populated
+ return sd_bus_reply_method_error(m, retError);
+ }
+
+ if (config.getCurrTimeOwner() == TimeConfig::timeOwners::SPLIT)
+ {
+ iv_HostOffset = time.getChangedOffset();
+ r = config.writeData<decltype(iv_HostOffset.count())>
+ (cv_HostOffsetFile,
+ iv_HostOffset.count());
+ if (r < 0)
+ {
+ // probably does not make sense to crash on these..
+ // The next NTP sync will set things right.
+ std::cerr << "Error saving host_offset: "
+ << iv_HostOffset.count() << std::endl;
+ }
+ }
+ }
+ return sd_bus_reply_method_return(m, "i", 0);
+}
+
+int Time::setTimeOfDay(const std::chrono::microseconds& timeOfDayUsec)
+{
+ // These 2 are for bypassing some policy
+ // checking in the timedate1 service
+ auto relative = false;
+ auto interactive = false;
+
+ return sd_bus_call_method(config.getDbus(),
+ "org.freedesktop.timedate1",
+ "/org/freedesktop/timedate1",
+ "org.freedesktop.timedate1",
+ "SetTime",
+ nullptr,
+ nullptr, // timedate1 does not return response
+ "xbb",
+ (int64_t)timeOfDayUsec.count(), //newTimeUsec,
+ relative, // Time in absolute seconds since epoch
+ interactive); // bypass polkit checks
+}
+
+// Common routine for BMC and HOST Get Time operations
+std::chrono::microseconds Time::getBaseTime()
+{
+ auto currBmcTime = std::chrono::system_clock::now();
+ return std::chrono::duration_cast<std::chrono::microseconds>
+ (currBmcTime.time_since_epoch());
+}
+
+// Accepts the time in microseconds and converts to Human readable format.
+std::string Time::convertToStr(const std::chrono::microseconds& timeInUsec)
+{
+ using namespace std::chrono;
+
+ // Convert this to number of seconds;
+ auto timeInSec = duration_cast<seconds>(microseconds(timeInUsec));
+ auto time_T = static_cast<std::time_t>(timeInSec.count());
+
+ std::ostringstream timeFormat {};
+ timeFormat << std::put_time(std::gmtime(&time_T), "%c %Z");
+
+ auto timeStr = timeFormat.str();
+ std::cout << timeStr.c_str() << std::endl;
+ return timeStr;
+}
+
+// Reads timeofday and returns time string
+// and also number of microseconds.
+// Ex : Tue Aug 16 22:49:43 2016
+int BmcTime::getTime(sd_bus_message *m, sd_bus_error *retError)
+{
+ std::cout << "Request to get BMC time: ";
+
+ // Get BMC time
+ auto timeInUsec = getBaseTime();
+ auto timeStr = convertToStr(timeInUsec);
+ return sd_bus_reply_method_return(
+ m, "sx", timeStr.c_str(), (uint64_t)timeInUsec.count());
+}
+
+// Designated to be called by IPMI_GET_SEL time from host
+int HostTime::getTime(sd_bus_message *m, sd_bus_error *retError)
+{
+ using namespace std::chrono;
+
+ std::cout << "Request to get HOST time" << std::endl;
+
+ // Get BMC time and add Host's offset
+ // Referencing the iv_hostOffset of TimeManager object
+ auto timeInUsec = getBaseTime();
+ auto hostTime = timeInUsec.count() + iv_Offset.count();
+
+ auto timeStr = convertToStr(duration_cast<microseconds>
+ (microseconds(hostTime)));
+
+ std::cout << " Host_time_str: [ " << timeStr
+ << " ] host_time usec: [ " << hostTime
+ << " ] host_offset: [ " << iv_Offset.count()
+ << " ] " << std::endl;
+
+ return sd_bus_reply_method_return(m, "sx", timeStr.c_str(),
+ (uint64_t)hostTime);
+ return 0;
+}
+
+// Gets the time string and verifies if it conforms to format %Y-%m-%d %H:%M:%S
+// and then sets the BMC time. If the input time string does not conform to the
+// format, an error message is returned.
+int BmcTime::setTime(sd_bus_message *m, sd_bus_error *retError)
+{
+ tm userTm {};
+ const char* userTimeStr = nullptr;
+
+ std::cout << "Request to set BMC time" << std::endl;
+
+ std::cout << "Curr_Mode: " << TimeConfig::modeStr(config.getCurrTimeMode())
+ << " Curr_Owner: " << TimeConfig::ownerStr(config.getCurrTimeOwner())
+ << std::endl;
+
+ if (config.getCurrTimeMode() == TimeConfig::timeModes::NTP)
+ {
+ std::cerr << "Can not set time. Mode is NTP" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_FAILED, "Current Mode is NTP");
+
+ return -1;
+ }
+
+ if(config.getCurrTimeOwner() == TimeConfig::timeOwners::HOST)
+ {
+ std::cerr << "Can not set time. Owner is HOST" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_FAILED, "Current owner is HOST");
+
+ return -1;
+ }
+
+ auto r = sd_bus_message_read(m, "s", &userTimeStr);
+ if (r < 0)
+ {
+ std::cerr << "Error:" << strerror(-r)
+ <<" reading user time" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_IO_ERROR, "Error reading input");
+ return r;
+ }
+
+ std::cout <<" BMC TIME : " << userTimeStr << std::endl;
+
+ // Convert the time string into tm structure
+ std::istringstream timeString {};
+ timeString.str(userTimeStr);
+ timeString >> std::get_time(&userTm, "%Y-%m-%d %H:%M:%S");
+ if (timeString.fail())
+ {
+ std::cerr << "Error: Incorrect time format" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_INVALID_ARGS, "Incorrect time format");
+ return -1;
+ }
+
+ // Convert the time structure into number of
+ // seconds maintained in GMT. Followed the same that is in
+ // systemd/timedate1
+ auto timeOfDay = timegm(&userTm);
+ if (timeOfDay < 0)
+ {
+ std::cerr <<"Error converting tm to seconds" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_FAILED, "Error converting tm to seconds");
+ return -1;
+ }
+
+ // Set REALTIME and also update hwclock
+ auto timeInUsec = std::chrono::microseconds(
+ std::chrono::seconds(timeOfDay));
+ r = setTimeOfDay(timeInUsec);
+ if (r < 0)
+ {
+ std::cerr <<"Error: " << strerror(-r)
+ << "setting time on BMC" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_FAILED, "Error setting time on BMC");
+ }
+ return r < 0 ? r : 0;
+}
+
+// Gets the time string from IPMI ( which is currently in seconds since epoch )
+// and then sets the BMC time / adjusts the offset depending on current owner
+// policy.
+int HostTime::setTime(sd_bus_message *m, sd_bus_error *retError)
+{
+ using namespace std::chrono;
+
+ std::cout << "Request to SET Host time" << std::endl;
+
+ std::cout << "Curr_Mode: " << TimeConfig::modeStr(config.getCurrTimeMode())
+ << "Curr_Owner: " << TimeConfig::ownerStr(config.getCurrTimeOwner())
+ << "host_offset: " << iv_Offset.count() << std::endl;
+
+ if (config.getCurrTimeOwner() == TimeConfig::timeOwners::BMC)
+ {
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_FAILED, "Current Owner is BMC");
+ return -1;
+ }
+
+ const char* newHostTime = nullptr;
+ auto r = sd_bus_message_read(m, "s", &newHostTime);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "reading host time" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_IO_ERROR, "Error reading input");
+ return -1;
+ }
+
+ // We need to convert the string input to decimal.
+ auto hostTimeSec = std::stol(std::string(newHostTime));
+
+ // And then to microseconds
+ auto hostTimeUsec = duration_cast<microseconds>(seconds(hostTimeSec));
+ std::cout << "setHostTime: HostTimeInUSec: "
+ << hostTimeUsec.count() << std::endl;
+
+ if (config.getCurrTimeOwner() == TimeConfig::timeOwners::SPLIT)
+ {
+ // Adjust the host offset
+ auto bmcTimeUsec = duration_cast<microseconds>
+ (system_clock::now().time_since_epoch());
+
+ // We are not doing any time settings in BMC
+ std::cout << "Updated: host_time: [ " << hostTimeUsec.count()
+ << " ] host_offset: [ " << iv_Offset.count()
+ << " ] " << std::endl;
+
+ // Communicate the offset back to manager to update needed.
+ changedOffset = hostTimeUsec - bmcTimeUsec;
+
+ return 0;
+ }
+
+ // We are okay to update time in as long as BMC is not the owner
+ r = setTimeOfDay(hostTimeUsec);
+ if (r < 0)
+ {
+ std::cerr <<"Error: " << strerror(-r)
+ << "setting HOST time" << std::endl;
+ *retError = SD_BUS_ERROR_MAKE_CONST(
+ SD_BUS_ERROR_FAILED, "Error setting time");
+ }
+
+ return r < 0 ? r : 0;
+}
+
+// Gets called into by sd_event on an activity seen on sd_bus
+int TimeManager::processSdBusMessage(sd_event_source* es, int fd,
+ uint32_t revents, void* userdata)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ auto r = sd_bus_process(tmgr->getTimeBus(), nullptr);
+ if (r < 0)
+ {
+ std::cerr <<"Error: " << strerror(-r)
+ <<" processing sd_bus message:" << std::endl;
+ }
+ return r;
+}
+
+// Gets called into by sd_event on any time SET event
+int TimeManager::processTimeChange(sd_event_source* es, int fd,
+ uint32_t revents, void* userdata)
+{
+ using namespace std::chrono;
+ std::cout << "BMC time changed" << std::endl;
+
+ auto tmgr = static_cast<TimeManager*>(userdata);
+
+ std::array<char, 64> time {};
+
+ // We are not interested in the data here. Need to read time again .
+ // So read until there is something here in the FD
+ while (read(fd, time.data(), time.max_size()) > 0);
+
+ std::cout <<" Curr_Mode: " << TimeConfig::modeStr(tmgr->config.getCurrTimeMode())
+ << " Curr_Owner : " << TimeConfig::ownerStr(tmgr->config.getCurrTimeOwner())
+ << " Host_Offset: " << tmgr->getHostOffset().count() << std::endl;
+
+ // Read the current BMC time and adjust the
+ // host time offset if the mode is SPLIT
+ if (tmgr->config.getCurrTimeOwner() == TimeConfig::timeOwners::SPLIT)
+ {
+ // Delta between REAL and Monotonic.
+ auto uptimeUsec = duration_cast<microseconds>
+ (system_clock::now().time_since_epoch() -
+ steady_clock::now().time_since_epoch());
+
+ auto deltaTimeUsec = uptimeUsec - tmgr->getUptimeUsec();
+
+ // If the BMC time goes backwards, then - of - will handle that.
+ auto newHostOffset = tmgr->getHostOffset() - deltaTimeUsec;
+ tmgr->updateHostOffset(newHostOffset);
+
+ std::cout << " UPDATED HOST_OFFSET: "
+ << tmgr->getHostOffset().count() << std::endl;
+ tmgr->updateUptimeUsec(uptimeUsec);
+
+ // Persist this
+ auto r = tmgr->config.writeData<decltype(tmgr->getHostOffset().count())>
+ (TimeManager::cv_HostOffsetFile,
+ tmgr->getHostOffset().count());
+ if (r < 0)
+ {
+ std::cerr << "Error saving host_offset: "
+ << tmgr->getHostOffset().count() << std::endl;
+ return r;
+ }
+ std::cout << " Updated: Host_Offset: "
+ << tmgr->getHostOffset().count() << std::endl;
+ }
+ return 0;
+}
+
+// Resets iv_HostOffset. Needed when we move away from SPLIT.
+int TimeManager::resetHostOffset()
+{
+ iv_HostOffset = std::chrono::microseconds(0);
+ auto r = config.writeData<decltype(iv_HostOffset.count())>
+ (cv_HostOffsetFile,
+ iv_HostOffset.count());
+ config.updateSplitModeFlag(false);
+ return r;
+}
+
+// Called by sd_event when Pgood is changed in Power
+int TimeManager::processPgoodChange(sd_bus_message* m, void* userdata,
+ sd_bus_error* retError)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ const char* key = nullptr;
+ const char* value = nullptr;
+
+ auto newPgood = -1;
+ auto r = 0;
+
+ std::cout <<" PGOOD has changed.." << std::endl;
+
+ // input data is "sa{sv}as" and we are just interested in a{sv}
+ r = sd_bus_message_skip(m, "s");
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r) <<
+ "skipping interface name in data" << std::endl;
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<"entering the dictionary" << std::endl;
+ return r;
+ }
+
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY,
+ "sv")) > 0)
+ {
+ r = sd_bus_message_read(m, "s", &key);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<" reading the key from dict" << std::endl;
+ // Can not continue here since the next
+ // enter would result in error anyway
+ return r;
+ }
+
+ if (!strcmp(key, "pgood"))
+ {
+ r = sd_bus_message_read(m, "v", "i", &newPgood);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "reading pgood" << std::endl;
+ return r;
+ }
+ r = tmgr->config.updatePropertyVal(key, std::to_string(newPgood));
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "processing pgood" << std::endl;
+ return r;
+ }
+ }
+ else
+ {
+ sd_bus_message_skip(m, "v");
+ }
+ }
+ return 0;
+}
+
+// Called by sd_event when Properties are changed in settingsd.
+// Interested in changes to 'timeMode', 'timeOwner' and 'use_dhcp_ntp'
+int TimeManager::processPropertyChange(sd_bus_message* m, void* userdata,
+ sd_bus_error* retError)
+{
+ auto tmgr = static_cast<TimeManager*>(userdata);
+ const char* key = nullptr;
+ const char* value = nullptr;
+ auto r = 0;
+
+ std::cout <<" User Settings have changed.." << std::endl;
+
+ // input data is "sa{sv}as" and we are just interested in a{sv}
+ r = sd_bus_message_skip(m, "s");
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r) <<
+ "skipping interface name in data" << std::endl;
+ goto finish;
+ }
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<"entering the dictionary" << std::endl;
+ goto finish;
+ }
+
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY,
+ "sv")) > 0)
+ {
+ r = sd_bus_message_read(m, "s", &key);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<" reading the key from dict" << std::endl;
+ // Can not continue here since the next
+ // enter would result in error anyway
+ goto finish;
+ }
+
+ if (!strcmp(key, "time_mode"))
+ {
+ r = sd_bus_message_read(m, "v", "s", &value);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "reading timeMode" << std::endl;
+ goto finish;
+ }
+ r = tmgr->config.updatePropertyVal(key, value);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "processing timeMode" << std::endl;
+ goto finish;
+ }
+ }
+ else if (!strcmp(key, "time_owner"))
+ {
+ r = sd_bus_message_read(m, "v", "s", &value);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "reading timeOwner" << std::endl;
+ goto finish;
+ }
+ r = tmgr->config.updatePropertyVal(key, value);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "processing time_owner" << std::endl;
+ goto finish;
+ }
+ else if (tmgr->config.isSplitModeChanged())
+ {
+ // Must have been a change away from mode SPLIT
+ tmgr->resetHostOffset();
+ }
+ }
+ else if (!strcmp(key, "use_dhcp_ntp"))
+ {
+ r = sd_bus_message_read(m, "v", "s", &value);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<"reading use_dhcp_ntp" << std::endl;
+ goto finish;
+ }
+ r = tmgr->config.updatePropertyVal(key, value);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "processing dhcp_ntp" << std::endl;
+ goto finish;
+ }
+ }
+ else
+ {
+ sd_bus_message_skip(m, "v");
+ }
+ }
+finish:
+ return r;
+}
+
+// Sets up callback handlers for activities on :
+// 1) user request on SD_BUS
+// 2) Time change
+// 3) Settings change
+// 4) System state change;
+int TimeManager::registerCallbackHandlers()
+{
+ constexpr auto WATCH_SETTING_CHANGE =
+ "type='signal',interface='org.freedesktop.DBus.Properties',"
+ "path='/org/openbmc/settings/host0',member='PropertiesChanged'";
+
+ constexpr auto WATCH_PGOOD_CHANGE =
+ "type='signal',interface='org.freedesktop.DBus.Properties',"
+ "path='/org/openbmc/control/power0',member='PropertiesChanged'";
+
+ // Extract the descriptor out of sd_bus construct.
+ auto sdBusFd = sd_bus_get_fd(iv_TimeBus);
+
+ auto r = sd_event_add_io(iv_Event, &iv_EventSource, sdBusFd, EPOLLIN,
+ processSdBusMessage, this);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<" adding sd_bus_message handler" << std::endl;
+ return r;
+ }
+
+ // Choose the MAX time that is possible to aviod mis fires.
+ itimerspec maxTime {};
+ maxTime.it_value.tv_sec = TIME_T_MAX;
+
+ auto timeFd = timerfd_create(CLOCK_REALTIME, 0);
+ if (timeFd < 0)
+ {
+ std::cerr << "Errorno: " << errno << " creating timerfd" << std::endl;
+ return -1;
+ }
+
+ r = timerfd_settime(timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
+ &maxTime, nullptr);
+ if (r)
+ {
+ std::cerr << "Errorno: " << errno << "Setting timerfd" << std::endl;
+ return -1;
+ }
+
+ // Wake me up *only* if someone SETS the time
+ r = sd_event_add_io(iv_Event, &iv_EventSource, timeFd, EPOLLIN,
+ processTimeChange, this);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "adding time_change handler" << std::endl;
+ return r;
+ }
+
+ // Watch for property changes in settingsd
+ r = sd_bus_add_match(iv_TimeBus, NULL, WATCH_SETTING_CHANGE,
+ processPropertyChange, this);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<" adding property change listener" << std::endl;
+ return r;
+ }
+
+ // Watch for state change. Only reliable one to count on is
+ // state of [pgood]. value of [1] meaning host is powering on / powered
+ // on. [0] means powered off.
+ r = sd_bus_add_match(iv_TimeBus, NULL, WATCH_PGOOD_CHANGE,
+ processPgoodChange, this);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << " adding pgood change listener" << std::endl;
+ }
+ return r;
+}
+
+int TimeManager::setupTimeManager()
+{
+ auto r = sd_bus_default_system(&iv_TimeBus);
+ if (r < 0)
+ {
+ std::cerr << "Error" << strerror(-r)
+ <<" connecting to system bus" << std::endl;
+ goto finish;
+ }
+
+ r = sd_bus_add_object_manager(iv_TimeBus, NULL, cv_ObjPath);
+ if (r < 0)
+ {
+ std::cerr << "Error" << strerror(-r)
+ <<" adding object manager" << std::endl;
+ goto finish;
+ }
+
+ std::cout <<"Registering dbus methods" << std::endl;
+ r = sd_bus_add_object_vtable(iv_TimeBus,
+ NULL,
+ cv_ObjPath,
+ cv_BusName,
+ timeServicesVtable,
+ this);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<" adding timer services vtable" << std::endl;
+ goto finish;
+ }
+
+ // create a sd_event object and add handlers
+ r = sd_event_default(&iv_Event);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ <<"creating an sd_event" << std::endl;
+ goto finish;
+ }
+
+ // Handlers called by sd_event when an activity
+ // is observed in event loop
+ r = registerCallbackHandlers();
+ if (r < 0)
+ {
+ std::cerr << "Error setting up callback handlers" << std::endl;
+ goto finish;
+ }
+
+ // Need to do this here since TimeConfig may update the necessary owners
+ r = config.processInitialSettings(iv_TimeBus);
+ if (r < 0)
+ {
+ std::cerr << "Error setting up configuration params" << std::endl;
+ goto finish;
+ }
+
+ // Read saved values from previous run
+ r = readPersistentData();
+ if (r < 0)
+ {
+ std::cerr << "Error reading persistent data" << std::endl;
+ goto finish;
+ }
+
+ // Claim the bus
+ r = sd_bus_request_name(iv_TimeBus, cv_BusName, 0);
+ if (r < 0)
+ {
+ std::cerr << "Error: " << strerror(-r)
+ << "acquiring service name" << std::endl;
+ goto finish;
+ }
+finish:
+ if (r < 0)
+ {
+ iv_EventSource = sd_event_source_unref(iv_EventSource);
+ iv_Event = sd_event_unref(iv_Event);
+ }
+ return r;
+}
+
+int TimeManager::readPersistentData()
+{
+ using namespace std::chrono;
+
+ // Get current host_offset
+ // When we reach here, TimeConfig would have been populated and would have
+ // applied the owner to SPLIT *if* the system allowed it. So check if we
+ // moved away from SPLIT and if so, make offset:0
+ if (config.isSplitModeChanged())
+ {
+ iv_HostOffset = microseconds(0);
+ auto r = config.writeData<decltype(iv_HostOffset.count())>
+ (cv_HostOffsetFile,
+ iv_HostOffset.count());
+ if (r < 0)
+ {
+ std::cerr <<" Error saving offset to file" << std::endl;
+ return r;
+ }
+ }
+ else
+ {
+ auto hostTimeOffset = config.readData<long long int>(cv_HostOffsetFile);
+ iv_HostOffset = microseconds(hostTimeOffset);
+ std::cout <<"Last known host_offset:" << hostTimeOffset << std::endl;
+ }
+
+ //How long was the FSP up prior to 'this' start
+ iv_UptimeUsec = duration_cast<microseconds>
+ (system_clock::now().time_since_epoch() -
+ steady_clock::now().time_since_epoch());
+ if (iv_UptimeUsec.count() < 0)
+ {
+ std::cerr <<"Error reading uptime" << std::endl;
+ return -1;
+ }
+ std::cout <<"Initial Uptime Usec: "
+ << iv_UptimeUsec.count() << std::endl;
+ return 0;
+}
+
+// Forever loop
+int TimeManager::waitForClientRequest()
+{
+ return sd_event_loop(iv_Event);
+}
+
+int main(int argc, char* argv[])
+{
+ auto tmgr = std::make_unique<TimeManager>();
+
+ // Wait for the work
+ auto r = tmgr->waitForClientRequest();
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/time-manager.hpp b/time-manager.hpp
new file mode 100644
index 0000000..5e32a72
--- /dev/null
+++ b/time-manager.hpp
@@ -0,0 +1,303 @@
+#include <systemd/sd-bus.h>
+#include <fstream>
+#include <string>
+#include <chrono>
+#include "time-config.hpp"
+
+/** @class Time
+ * @brief Base class catering to common data needed by implementations
+ */
+class Time
+{
+public:
+ /** @brief Takes time in microseconds and uses systemd/timedated
+ * to set the system time
+ *
+ * @param[in] timeOfDayUsec - Time value in microseconds
+ * @return - Status of time set operation
+ */
+ int setTimeOfDay(const std::chrono::microseconds& timeOfDayUsec);
+
+ /** @brief Reads BMC time
+ *
+ * @param[in] - None
+ * @return - time read in microseconds
+ */
+ std::chrono::microseconds getBaseTime();
+
+ /** @brief Converts microseconds to human readable time value
+ *
+ * @param[in] timeInUsec - Time value in microseconds
+ * @return - Time converted to human readable format
+ */
+ std::string convertToStr(const std::chrono::microseconds& timeInUsec);
+
+ /** @brief Reference to config information due to need of policy data */
+ const TimeConfig& config;
+
+ /** Needed to access configuration variables */
+ Time(const TimeConfig&);
+
+private:
+ Time();
+};
+
+/** @class BmcTime
+ * @brief Provides time Set and time Get operations for BMC target
+ */
+class BmcTime: public Time
+{
+public:
+ /** @brief Called when the time is to be read on BMC
+ *
+ * @param[in] m - sd_bus message.
+ * @param[out] retError - Error reporting structure
+ * @return - On no error, time read which is specified in
+ * microseonds and also in human readable format.
+ *
+ * - On error, retError populated
+ */
+ int getTime(sd_bus_message* m, sd_bus_error* retError);
+
+ /** @brief Called when the time is to be set on BMC
+ *
+ * @param[in] m - sd_bus message encapsulating time string
+ * @param[out] retError - Error reporting structure
+ * @return - On no error, 0, -1 otherwise or retError thrown
+ */
+ int setTime(sd_bus_message* m, sd_bus_error* retError);
+
+ /** @brief constructor */
+ BmcTime(const TimeConfig&);
+
+private:
+ BmcTime();
+};
+
+/** @class HostTime
+ * @brief Provides time Set and time Get operations for BMC target
+ */
+class HostTime: public Time
+{
+public:
+ /** @brief Called when IPMI_GET_SEL_TIME is called
+ *
+ * @param[in] m - sd_bus message.
+ * @param[out] retError - Error reporting structure
+ * @return - On no error, time read which is specified in
+ * microseonds and also in human readable format.
+ *
+ * - On error, retError populated
+ */
+ int getTime(sd_bus_message*, sd_bus_error*);
+
+ /** @brief Called when the IPMI_SET_SEL_TIME is called
+ *
+ * @param[in] m - sd_bus message encapsulating time in seconds
+ * @param[out] retError - Error reporting structure
+ * @return - On no error, 0, -1 otherwise or retError thrown
+ */
+ int setTime(sd_bus_message*, sd_bus_error*);
+
+ /** @brief constructor */
+ HostTime(const TimeConfig&, const std::chrono::microseconds&);
+
+ /** @brief When the owner is SPLIT, the delta between HOST's time and BMC's
+ * time needs to be saved and this function returns current delta.
+ *
+ * @param[in] - None
+ * @return - offset in microseconds
+ */
+ inline std::chrono::microseconds getChangedOffset() const
+ {
+ return changedOffset;
+ }
+
+ /** @brief Reference to host's offset in case of SPLIT owner */
+ const std::chrono::microseconds& iv_Offset;
+
+private:
+ HostTime();
+
+ /** @brief The delta offset of Host and BMC time */
+ std::chrono::microseconds changedOffset;
+};
+
+/** @class TimeManager
+ * @brief Caters to client requests with Set and Get time and configuration
+ * changes
+ */
+class TimeManager
+{
+public:
+ // Do not have a usecase of copying this object so disable
+ TimeManager();
+ ~TimeManager() = default;
+ TimeManager(const TimeManager&) = delete;
+ TimeManager& operator=(const TimeManager&) = delete;
+ TimeManager(TimeManager&&) = delete;
+ TimeManager& operator=(TimeManager&&) = delete;
+
+ // Maintains *all* the config that is needed for TimeManager.
+ TimeConfig config;
+
+ /** @brief Callback handlers invoked by dbus on GetTime client requests
+ *
+ * @param[in] m - sd_bus message encapsulating the time target
+ * @param[in] userdata - context that is filled while registering this
+ * @param[out] retError - Error reporting mechanism
+ *
+ * @return - On no error, time in microseconds and human
+ * readable string. retError otherwise.
+ */
+ int getTime(sd_bus_message* m, void* userdata, sd_bus_error* retError);
+
+ /** @brief Callback handlers invoked by dbus on SetTime client requests
+ *
+ * @param[in] m - sd_bus message encapsulating the time target and
+ * time value either in string or in seconds.
+ * @param[in] userdata - client context that is filled while registering
+ * @param[out] retError - Error reporting mechanism
+ *
+ * @return - On no error, time in microseconds and human
+ * readable string. retError otherwise.
+ */
+ int setTime(sd_bus_message* m, void* userdata, sd_bus_error* retError);
+
+ /** @brief sd_event callback handlers on the requests coming in dbus
+ * These are actually GetTime and SetTime requests
+ *
+ * @param[in] es - Event Structure
+ * @param[in] fd - file descriptor that had 'read' activity
+ * @param[in] revents - generic linux style return event
+ * @param[in] userdata - Client context filled while registering
+ *
+ * @return - 0 for success, failure otherwise.
+ */
+ static int processSdBusMessage(sd_event_source* es, int fd,
+ uint32_t revents, void* userdata);
+
+ /** @brief sd_event callback handler called whenever there is a
+ * time change event indicated by timerfd expiring. This happens
+ * whenever the time is set on BMC by any source.
+ *
+ * @param[in] es - Event Structure
+ * @param[in] fd - file descriptor that had 'read' activity
+ * @param[in] revents - generic linux style return event
+ * @param[in] userdata - Client context filled while registering
+ *
+ * @return - 0 for success, failure otherwise.
+ */
+ static int processTimeChange(sd_event_source* es, int fd,
+ uint32_t revents, void* userdata);
+
+ /** @brief sd_event callback handler called whenever a settings
+ * property is changed.
+ * This gets called into whenever "time_mode", "time_owner",
+ * "use_dhcp_ntp" properties are changed
+ *
+ * @param[in] es - Event Structure
+ * @param[in] fd - file descriptor that had 'read' activity
+ * @param[in] revents - generic linux style return event
+ * @param[in] userdata - Client context filled while registering
+ *
+ * @return - 0 for success, failure otherwise.
+ */
+ static int processPropertyChange(sd_bus_message*,
+ void*,sd_bus_error*);
+
+ /** @brief sd_event callback handler called whenever Pgood property is
+ * changed
+ *
+ * @param[in] es - Event Structure
+ * @param[in] fd - file descriptor that had 'read' activity
+ * @param[in] revents - generic linux style return event
+ * @param[in] userdata - Client context filled while registering
+ *
+ * @return - 0 for success, failure otherwise.
+ */
+ static int processPgoodChange(sd_bus_message*,
+ void*,sd_bus_error*);
+
+ /** @brief registers callsback handlers for sd_event loop
+ *
+ * @param[in] - None
+ * @return - 0 if everything goes well, -1 otherwise
+ */
+ int registerCallbackHandlers();
+
+ /** @brief Makes the Delta between Host and BMC time as 'ZERO'. This
+ * essentially only means that time owner was SPLIT before
+ * and now changed to something else.
+ *
+ * @param[in] - None
+ * @return - 0 if everything goes well, -1 otherwise.
+ */
+ int resetHostOffset();
+
+ /** @brief Reads what was the last delta offset stored in file
+ *
+ * @param[in] - None
+ * @return - 0 if everything goes well, -1 otherwise.
+ */
+ int readPersistentData();
+
+ /** @brief waits on sd_events loop for client requests
+ *
+ * @param[in] - None
+ * @return - 0 if everything goes well, -1 otherwise.
+ */
+ int waitForClientRequest();
+
+ inline auto getHostOffset() const
+ {
+ return iv_HostOffset;
+ }
+
+ inline auto updateHostOffset(const std::chrono::microseconds& newOffset)
+ {
+ iv_HostOffset = newOffset;
+ }
+
+ inline auto getUptimeUsec() const
+ {
+ return iv_UptimeUsec;
+ }
+
+ inline auto updateUptimeUsec(const std::chrono::microseconds& newUpTime)
+ {
+ iv_UptimeUsec = newUpTime;
+ }
+
+ inline sd_bus* getTimeBus() const
+ {
+ return iv_TimeBus;
+ }
+
+private:
+ // What was the last known host offset.
+ std::chrono::microseconds iv_HostOffset;
+
+ // How long was the BMC up for prior to this boot
+ std::chrono::microseconds iv_UptimeUsec;
+
+ // Used for registering sd_bus callback handlers.
+ sd_event_source* iv_EventSource;
+ sd_event* iv_Event;
+ sd_bus* iv_TimeBus;
+
+ // Dbus communication enablers.
+ static constexpr auto cv_BusName = "org.openbmc.TimeManager";
+ static constexpr auto cv_ObjPath = "/org/openbmc/TimeManager";
+ static constexpr auto cv_IntfName = "org.openbmc.TimeManager";
+
+ // Store the offset in File System. Read back when TimeManager starts.
+ static constexpr auto cv_HostOffsetFile = "/var/lib/obmc/saved_host_offset";
+
+ /** @brief Sets up internal data structures and callback handler at startup
+ *
+ * @param[in] - None
+ * @return - 0 if everything goes well, -1 otherwise
+ */
+ int setupTimeManager(void);
+};
diff --git a/time-register.c b/time-register.c
new file mode 100644
index 0000000..cdc1dd2
--- /dev/null
+++ b/time-register.c
@@ -0,0 +1,25 @@
+#include <systemd/sd-bus.h>
+#include "time-register.hpp"
+#include <sys/timerfd.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* Function pointer of APIs exposed via Dbus */
+//const sd_bus_vtable timeServicesVtable[] =
+const sd_bus_vtable timeServicesVtable [] =
+{
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_PROPERTY("curr_time_mode", "s", getCurrTimeModeProperty, 0,
+ SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("curr_time_owner", "s", getCurrTimeOwnerProperty, 0,
+ SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("requested_time_mode", "s", getReqTimeModeProperty, 0,
+ SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("requested_time_owner", "s", getReqTimeOwnerProperty, 0,
+ SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_METHOD("GetTime", "s", "sx", &GetTime,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetTime", "ss", "i", &SetTime,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_VTABLE_END,
+};
diff --git a/time-register.hpp b/time-register.hpp
new file mode 100644
index 0000000..bf776d3
--- /dev/null
+++ b/time-register.hpp
@@ -0,0 +1,17 @@
+#include <systemd/sd-bus.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+int getCurrTimeModeProperty(sd_bus*, const char*, const char*, const char*,
+ sd_bus_message*, void*, sd_bus_error*);
+int getCurrTimeOwnerProperty(sd_bus*, const char*, const char*, const char*,
+ sd_bus_message*, void*, sd_bus_error*);
+int getReqTimeModeProperty(sd_bus*, const char*, const char*, const char*,
+ sd_bus_message*, void*, sd_bus_error*);
+int getReqTimeOwnerProperty(sd_bus*, const char*, const char*, const char*,
+ sd_bus_message*, void*, sd_bus_error*);
+int GetTime(sd_bus_message*, void*, sd_bus_error*);
+int SetTime(sd_bus_message*, void*, sd_bus_error*);
+#ifdef __cplusplus
+};
+#endif
OpenPOWER on IntegriCloud