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