summaryrefslogtreecommitdiffstats
path: root/time-manager.hpp
blob: 23560a40f3ec9f017f39aa6ac9becb3857b1d389 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#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
     *  @param[out] retError     - Error message on failure
     *  @return                  - Status of time set operation
     *  @error                   - Error message populated
     */
    int setTimeOfDay(const std::chrono::microseconds& timeOfDayUsec,
                     sd_bus_error *retError);

    /** @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);
};
OpenPOWER on IntegriCloud