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>
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);
+};