blob: bde2e0da84ac066f6a8952a46cce15559b033861 [file] [log] [blame]
Vishwanatha81ee91f2016-08-30 17:17:13 +05301#define _XOPEN_SOURCE
2#include <chrono>
3#include <sstream>
4#include <iostream>
5#include <iomanip>
6#include <array>
7#include <unistd.h>
8#include <assert.h>
9#include <sys/timerfd.h>
10#include <systemd/sd-event.h>
11#include <systemd/sd-bus.h>
12#include <systemd/sd-daemon.h>
13#include "time-register.hpp"
14#include "time-manager.hpp"
15
16// Neeed to do this since its not exported outside of the kernel.
17// Refer : https://gist.github.com/lethean/446cea944b7441228298
18#ifndef TFD_TIMER_CANCEL_ON_SET
19#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
20#endif
21
22// Needed to make sure timerfd does not misfire eventhough we set CANCEL_ON_SET
23#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
24
25// Used in time-register.c
26extern sd_bus_vtable timeServicesVtable[];
27
28Time::Time(const TimeConfig& timeConfig) : config(timeConfig)
29{
30 // Nothing to do here
31}
32
33BmcTime::BmcTime(const TimeConfig& timeConfig) : Time(timeConfig)
34{
35 // Nothing to do here
36}
37
38HostTime::HostTime(const TimeConfig& timeConfig,
39 const std::chrono::microseconds& hostOffset)
40 : Time(timeConfig),
41 iv_Offset(hostOffset)
42{
43 // Nothing to do here
44}
45
46TimeManager::TimeManager() :
47 iv_HostOffset(std::chrono::microseconds(0)),
48 iv_UptimeUsec(std::chrono::microseconds(0)),
49 iv_EventSource(nullptr),
50 iv_Event(nullptr)
51{
52 assert(setupTimeManager() >= 0);
53}
54
55// Needed to be standalone extern "C" to register
56// as a callback routine with sd_bus_vtable
57int GetTime(sd_bus_message* m, void* userdata,
58 sd_bus_error* retError)
59{
60 auto tmgr = static_cast<TimeManager*>(userdata);
61 return tmgr->getTime(m, userdata, retError);
62}
63
64int SetTime(sd_bus_message* m, void* userdata,
65 sd_bus_error* retError)
66{
67 auto tmgr = static_cast<TimeManager*>(userdata);
68 return tmgr->setTime(m, userdata, retError);
69}
70
71// Property reader
72int getCurrTimeModeProperty(sd_bus* bus, const char* path,
73 const char* interface, const char* property,
74 sd_bus_message* m, void* userdata,
75 sd_bus_error* error)
76{
77 auto tmgr = static_cast<TimeManager*>(userdata);
78 return sd_bus_message_append(m, "s",
79 TimeConfig::modeStr(tmgr->config.getCurrTimeMode()));
80}
81
82int getCurrTimeOwnerProperty(sd_bus* bus, const char* path,
83 const char* interface, const char* property,
84 sd_bus_message* m, void* userdata,
85 sd_bus_error* error)
86{
87 auto tmgr = static_cast<TimeManager*>(userdata);
88 return sd_bus_message_append(m, "s",
89 TimeConfig::ownerStr(tmgr->config.getCurrTimeOwner()));
90}
91
92int getReqTimeModeProperty(sd_bus* bus, const char* path,
93 const char* interface, const char* property,
94 sd_bus_message* m, void* userdata,
95 sd_bus_error* error)
96{
97 auto tmgr = static_cast<TimeManager*>(userdata);
98 return sd_bus_message_append(m, "s",
99 TimeConfig::modeStr(tmgr->config.getRequestedTimeMode()));
100}
101
102int getReqTimeOwnerProperty(sd_bus* bus, const char* path,
103 const char* interface, const char* property,
104 sd_bus_message* m, void* userdata,
105 sd_bus_error* error)
106{
107 auto tmgr = static_cast<TimeManager*>(userdata);
108 return sd_bus_message_append(m, "s",
109 TimeConfig::ownerStr(tmgr->config.getRequestedTimeOwner()));
110}
111
112int TimeManager::getTime(sd_bus_message* m, void* userdata,
113 sd_bus_error* retError)
114{
115 const char* target = nullptr;
116
117 // Extract the target and call respective GetTime
118 auto r = sd_bus_message_read(m, "s", &target);
119 if (r < 0)
120 {
121 std::cerr << "Error:" << strerror(-r)
122 <<" reading user time" << std::endl;
123 *retError = SD_BUS_ERROR_MAKE_CONST(
124 SD_BUS_ERROR_IO_ERROR, "Error reading input");
125
126 return sd_bus_reply_method_error(m, retError);
127 }
128
129 if (!strcasecmp(target, "bmc"))
130 {
131 auto time = BmcTime(config);
132 return time.getTime(m, retError);
133 }
134 else if (!strcasecmp(target, "host"))
135 {
136 auto time = HostTime(config, iv_HostOffset);
137 return time.getTime(m, retError);
138 }
139 else
140 {
141 std::cerr << "Error:" << strerror(-r)
142 <<" Invalid Target" << std::endl;
143 *retError = SD_BUS_ERROR_MAKE_CONST(
144 SD_BUS_ERROR_IO_ERROR, "Valid targets are BMC or HOST");
145
146 return sd_bus_reply_method_error(m, retError);
147 }
148}
149
150int TimeManager::setTime(sd_bus_message* m, void* userdata,
151 sd_bus_error* retError)
152{
153 long long int timeInUsec {};
154
155 const char* target = nullptr;
156 auto r = sd_bus_message_read(m, "s", &target);
157 if (r < 0)
158 {
159 std::cerr << "Error:" << strerror(-r)
160 <<" reading user time" << std::endl;
161 *retError = SD_BUS_ERROR_MAKE_CONST(
162 SD_BUS_ERROR_IO_ERROR, "Error reading input");
163
164 return sd_bus_reply_method_error(m, retError);
165 }
166
167 if (!strcasecmp(target, "bmc"))
168 {
169 auto time = BmcTime(config);
170 auto r = time.setTime(m, retError);
171 if (r < 0)
172 {
173 // This would have the error populated
174 return sd_bus_reply_method_error(m, retError);
175 }
176 }
177 else if (!strcasecmp(target, "host"))
178 {
179 auto time = HostTime(config, iv_HostOffset);
180
181 auto r = time.setTime(m, retError);
182 if (r < 0)
183 {
184 // This would have the error populated
185 return sd_bus_reply_method_error(m, retError);
186 }
187
188 if (config.getCurrTimeOwner() == TimeConfig::timeOwners::SPLIT)
189 {
190 iv_HostOffset = time.getChangedOffset();
191 r = config.writeData<decltype(iv_HostOffset.count())>
192 (cv_HostOffsetFile,
193 iv_HostOffset.count());
194 if (r < 0)
195 {
196 // probably does not make sense to crash on these..
197 // The next NTP sync will set things right.
198 std::cerr << "Error saving host_offset: "
199 << iv_HostOffset.count() << std::endl;
200 }
201 }
202 }
203 return sd_bus_reply_method_return(m, "i", 0);
204}
205
206int Time::setTimeOfDay(const std::chrono::microseconds& timeOfDayUsec)
207{
208 // These 2 are for bypassing some policy
209 // checking in the timedate1 service
210 auto relative = false;
211 auto interactive = false;
212
213 return sd_bus_call_method(config.getDbus(),
214 "org.freedesktop.timedate1",
215 "/org/freedesktop/timedate1",
216 "org.freedesktop.timedate1",
217 "SetTime",
218 nullptr,
219 nullptr, // timedate1 does not return response
220 "xbb",
221 (int64_t)timeOfDayUsec.count(), //newTimeUsec,
222 relative, // Time in absolute seconds since epoch
223 interactive); // bypass polkit checks
224}
225
226// Common routine for BMC and HOST Get Time operations
227std::chrono::microseconds Time::getBaseTime()
228{
229 auto currBmcTime = std::chrono::system_clock::now();
230 return std::chrono::duration_cast<std::chrono::microseconds>
231 (currBmcTime.time_since_epoch());
232}
233
234// Accepts the time in microseconds and converts to Human readable format.
235std::string Time::convertToStr(const std::chrono::microseconds& timeInUsec)
236{
237 using namespace std::chrono;
238
239 // Convert this to number of seconds;
240 auto timeInSec = duration_cast<seconds>(microseconds(timeInUsec));
241 auto time_T = static_cast<std::time_t>(timeInSec.count());
242
243 std::ostringstream timeFormat {};
244 timeFormat << std::put_time(std::gmtime(&time_T), "%c %Z");
245
246 auto timeStr = timeFormat.str();
247 std::cout << timeStr.c_str() << std::endl;
248 return timeStr;
249}
250
251// Reads timeofday and returns time string
252// and also number of microseconds.
253// Ex : Tue Aug 16 22:49:43 2016
254int BmcTime::getTime(sd_bus_message *m, sd_bus_error *retError)
255{
256 std::cout << "Request to get BMC time: ";
257
258 // Get BMC time
259 auto timeInUsec = getBaseTime();
260 auto timeStr = convertToStr(timeInUsec);
261 return sd_bus_reply_method_return(
262 m, "sx", timeStr.c_str(), (uint64_t)timeInUsec.count());
263}
264
265// Designated to be called by IPMI_GET_SEL time from host
266int HostTime::getTime(sd_bus_message *m, sd_bus_error *retError)
267{
268 using namespace std::chrono;
269
270 std::cout << "Request to get HOST time" << std::endl;
271
272 // Get BMC time and add Host's offset
273 // Referencing the iv_hostOffset of TimeManager object
274 auto timeInUsec = getBaseTime();
275 auto hostTime = timeInUsec.count() + iv_Offset.count();
276
277 auto timeStr = convertToStr(duration_cast<microseconds>
278 (microseconds(hostTime)));
279
280 std::cout << " Host_time_str: [ " << timeStr
281 << " ] host_time usec: [ " << hostTime
282 << " ] host_offset: [ " << iv_Offset.count()
283 << " ] " << std::endl;
284
285 return sd_bus_reply_method_return(m, "sx", timeStr.c_str(),
286 (uint64_t)hostTime);
287 return 0;
288}
289
290// Gets the time string and verifies if it conforms to format %Y-%m-%d %H:%M:%S
291// and then sets the BMC time. If the input time string does not conform to the
292// format, an error message is returned.
293int BmcTime::setTime(sd_bus_message *m, sd_bus_error *retError)
294{
295 tm userTm {};
296 const char* userTimeStr = nullptr;
297
298 std::cout << "Request to set BMC time" << std::endl;
299
300 std::cout << "Curr_Mode: " << TimeConfig::modeStr(config.getCurrTimeMode())
301 << " Curr_Owner: " << TimeConfig::ownerStr(config.getCurrTimeOwner())
302 << std::endl;
303
304 if (config.getCurrTimeMode() == TimeConfig::timeModes::NTP)
305 {
306 std::cerr << "Can not set time. Mode is NTP" << std::endl;
307 *retError = SD_BUS_ERROR_MAKE_CONST(
308 SD_BUS_ERROR_FAILED, "Current Mode is NTP");
309
310 return -1;
311 }
312
313 if(config.getCurrTimeOwner() == TimeConfig::timeOwners::HOST)
314 {
315 std::cerr << "Can not set time. Owner is HOST" << std::endl;
316 *retError = SD_BUS_ERROR_MAKE_CONST(
317 SD_BUS_ERROR_FAILED, "Current owner is HOST");
318
319 return -1;
320 }
321
322 auto r = sd_bus_message_read(m, "s", &userTimeStr);
323 if (r < 0)
324 {
325 std::cerr << "Error:" << strerror(-r)
326 <<" reading user time" << std::endl;
327 *retError = SD_BUS_ERROR_MAKE_CONST(
328 SD_BUS_ERROR_IO_ERROR, "Error reading input");
329 return r;
330 }
331
332 std::cout <<" BMC TIME : " << userTimeStr << std::endl;
333
334 // Convert the time string into tm structure
335 std::istringstream timeString {};
336 timeString.str(userTimeStr);
337 timeString >> std::get_time(&userTm, "%Y-%m-%d %H:%M:%S");
338 if (timeString.fail())
339 {
340 std::cerr << "Error: Incorrect time format" << std::endl;
341 *retError = SD_BUS_ERROR_MAKE_CONST(
342 SD_BUS_ERROR_INVALID_ARGS, "Incorrect time format");
343 return -1;
344 }
345
346 // Convert the time structure into number of
347 // seconds maintained in GMT. Followed the same that is in
348 // systemd/timedate1
349 auto timeOfDay = timegm(&userTm);
350 if (timeOfDay < 0)
351 {
352 std::cerr <<"Error converting tm to seconds" << std::endl;
353 *retError = SD_BUS_ERROR_MAKE_CONST(
354 SD_BUS_ERROR_FAILED, "Error converting tm to seconds");
355 return -1;
356 }
357
358 // Set REALTIME and also update hwclock
359 auto timeInUsec = std::chrono::microseconds(
360 std::chrono::seconds(timeOfDay));
361 r = setTimeOfDay(timeInUsec);
362 if (r < 0)
363 {
364 std::cerr <<"Error: " << strerror(-r)
365 << "setting time on BMC" << std::endl;
366 *retError = SD_BUS_ERROR_MAKE_CONST(
367 SD_BUS_ERROR_FAILED, "Error setting time on BMC");
368 }
369 return r < 0 ? r : 0;
370}
371
372// Gets the time string from IPMI ( which is currently in seconds since epoch )
373// and then sets the BMC time / adjusts the offset depending on current owner
374// policy.
375int HostTime::setTime(sd_bus_message *m, sd_bus_error *retError)
376{
377 using namespace std::chrono;
378
379 std::cout << "Request to SET Host time" << std::endl;
380
381 std::cout << "Curr_Mode: " << TimeConfig::modeStr(config.getCurrTimeMode())
382 << "Curr_Owner: " << TimeConfig::ownerStr(config.getCurrTimeOwner())
383 << "host_offset: " << iv_Offset.count() << std::endl;
384
385 if (config.getCurrTimeOwner() == TimeConfig::timeOwners::BMC)
386 {
387 *retError = SD_BUS_ERROR_MAKE_CONST(
388 SD_BUS_ERROR_FAILED, "Current Owner is BMC");
389 return -1;
390 }
391
392 const char* newHostTime = nullptr;
393 auto r = sd_bus_message_read(m, "s", &newHostTime);
394 if (r < 0)
395 {
396 std::cerr << "Error: " << strerror(-r)
397 << "reading host time" << std::endl;
398 *retError = SD_BUS_ERROR_MAKE_CONST(
399 SD_BUS_ERROR_IO_ERROR, "Error reading input");
400 return -1;
401 }
402
403 // We need to convert the string input to decimal.
404 auto hostTimeSec = std::stol(std::string(newHostTime));
405
406 // And then to microseconds
407 auto hostTimeUsec = duration_cast<microseconds>(seconds(hostTimeSec));
408 std::cout << "setHostTime: HostTimeInUSec: "
409 << hostTimeUsec.count() << std::endl;
410
411 if (config.getCurrTimeOwner() == TimeConfig::timeOwners::SPLIT)
412 {
413 // Adjust the host offset
414 auto bmcTimeUsec = duration_cast<microseconds>
415 (system_clock::now().time_since_epoch());
416
417 // We are not doing any time settings in BMC
418 std::cout << "Updated: host_time: [ " << hostTimeUsec.count()
419 << " ] host_offset: [ " << iv_Offset.count()
420 << " ] " << std::endl;
421
422 // Communicate the offset back to manager to update needed.
423 changedOffset = hostTimeUsec - bmcTimeUsec;
424
425 return 0;
426 }
427
428 // We are okay to update time in as long as BMC is not the owner
429 r = setTimeOfDay(hostTimeUsec);
430 if (r < 0)
431 {
432 std::cerr <<"Error: " << strerror(-r)
433 << "setting HOST time" << std::endl;
434 *retError = SD_BUS_ERROR_MAKE_CONST(
435 SD_BUS_ERROR_FAILED, "Error setting time");
436 }
437
438 return r < 0 ? r : 0;
439}
440
441// Gets called into by sd_event on an activity seen on sd_bus
442int TimeManager::processSdBusMessage(sd_event_source* es, int fd,
443 uint32_t revents, void* userdata)
444{
445 auto tmgr = static_cast<TimeManager*>(userdata);
446 auto r = sd_bus_process(tmgr->getTimeBus(), nullptr);
447 if (r < 0)
448 {
449 std::cerr <<"Error: " << strerror(-r)
450 <<" processing sd_bus message:" << std::endl;
451 }
452 return r;
453}
454
455// Gets called into by sd_event on any time SET event
456int TimeManager::processTimeChange(sd_event_source* es, int fd,
457 uint32_t revents, void* userdata)
458{
459 using namespace std::chrono;
460 std::cout << "BMC time changed" << std::endl;
461
462 auto tmgr = static_cast<TimeManager*>(userdata);
463
464 std::array<char, 64> time {};
465
466 // We are not interested in the data here. Need to read time again .
467 // So read until there is something here in the FD
468 while (read(fd, time.data(), time.max_size()) > 0);
469
470 std::cout <<" Curr_Mode: " << TimeConfig::modeStr(tmgr->config.getCurrTimeMode())
471 << " Curr_Owner : " << TimeConfig::ownerStr(tmgr->config.getCurrTimeOwner())
472 << " Host_Offset: " << tmgr->getHostOffset().count() << std::endl;
473
474 // Read the current BMC time and adjust the
475 // host time offset if the mode is SPLIT
476 if (tmgr->config.getCurrTimeOwner() == TimeConfig::timeOwners::SPLIT)
477 {
478 // Delta between REAL and Monotonic.
479 auto uptimeUsec = duration_cast<microseconds>
480 (system_clock::now().time_since_epoch() -
481 steady_clock::now().time_since_epoch());
482
483 auto deltaTimeUsec = uptimeUsec - tmgr->getUptimeUsec();
484
485 // If the BMC time goes backwards, then - of - will handle that.
486 auto newHostOffset = tmgr->getHostOffset() - deltaTimeUsec;
487 tmgr->updateHostOffset(newHostOffset);
488
489 std::cout << " UPDATED HOST_OFFSET: "
490 << tmgr->getHostOffset().count() << std::endl;
491 tmgr->updateUptimeUsec(uptimeUsec);
492
493 // Persist this
494 auto r = tmgr->config.writeData<decltype(tmgr->getHostOffset().count())>
495 (TimeManager::cv_HostOffsetFile,
496 tmgr->getHostOffset().count());
497 if (r < 0)
498 {
499 std::cerr << "Error saving host_offset: "
500 << tmgr->getHostOffset().count() << std::endl;
501 return r;
502 }
503 std::cout << " Updated: Host_Offset: "
504 << tmgr->getHostOffset().count() << std::endl;
505 }
506 return 0;
507}
508
509// Resets iv_HostOffset. Needed when we move away from SPLIT.
510int TimeManager::resetHostOffset()
511{
512 iv_HostOffset = std::chrono::microseconds(0);
513 auto r = config.writeData<decltype(iv_HostOffset.count())>
514 (cv_HostOffsetFile,
515 iv_HostOffset.count());
516 config.updateSplitModeFlag(false);
517 return r;
518}
519
520// Called by sd_event when Pgood is changed in Power
521int TimeManager::processPgoodChange(sd_bus_message* m, void* userdata,
522 sd_bus_error* retError)
523{
524 auto tmgr = static_cast<TimeManager*>(userdata);
525 const char* key = nullptr;
526 const char* value = nullptr;
527
528 auto newPgood = -1;
529 auto r = 0;
530
531 std::cout <<" PGOOD has changed.." << std::endl;
532
533 // input data is "sa{sv}as" and we are just interested in a{sv}
534 r = sd_bus_message_skip(m, "s");
535 if (r < 0)
536 {
537 std::cerr << "Error: " << strerror(-r) <<
538 "skipping interface name in data" << std::endl;
539 return r;
540 }
541
542 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
543 if (r < 0)
544 {
545 std::cerr << "Error: " << strerror(-r)
546 <<"entering the dictionary" << std::endl;
547 return r;
548 }
549
550 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY,
551 "sv")) > 0)
552 {
553 r = sd_bus_message_read(m, "s", &key);
554 if (r < 0)
555 {
556 std::cerr << "Error: " << strerror(-r)
557 <<" reading the key from dict" << std::endl;
558 // Can not continue here since the next
559 // enter would result in error anyway
560 return r;
561 }
562
563 if (!strcmp(key, "pgood"))
564 {
565 r = sd_bus_message_read(m, "v", "i", &newPgood);
566 if (r < 0)
567 {
568 std::cerr << "Error: " << strerror(-r)
569 << "reading pgood" << std::endl;
570 return r;
571 }
572 r = tmgr->config.updatePropertyVal(key, std::to_string(newPgood));
573 if (r < 0)
574 {
575 std::cerr << "Error: " << strerror(-r)
576 << "processing pgood" << std::endl;
577 return r;
578 }
579 }
580 else
581 {
582 sd_bus_message_skip(m, "v");
583 }
584 }
585 return 0;
586}
587
588// Called by sd_event when Properties are changed in settingsd.
589// Interested in changes to 'timeMode', 'timeOwner' and 'use_dhcp_ntp'
590int TimeManager::processPropertyChange(sd_bus_message* m, void* userdata,
591 sd_bus_error* retError)
592{
593 auto tmgr = static_cast<TimeManager*>(userdata);
594 const char* key = nullptr;
595 const char* value = nullptr;
596 auto r = 0;
597
598 std::cout <<" User Settings have changed.." << std::endl;
599
600 // input data is "sa{sv}as" and we are just interested in a{sv}
601 r = sd_bus_message_skip(m, "s");
602 if (r < 0)
603 {
604 std::cerr << "Error: " << strerror(-r) <<
605 "skipping interface name in data" << std::endl;
606 goto finish;
607 }
608
609 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
610 if (r < 0)
611 {
612 std::cerr << "Error: " << strerror(-r)
613 <<"entering the dictionary" << std::endl;
614 goto finish;
615 }
616
617 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY,
618 "sv")) > 0)
619 {
620 r = sd_bus_message_read(m, "s", &key);
621 if (r < 0)
622 {
623 std::cerr << "Error: " << strerror(-r)
624 <<" reading the key from dict" << std::endl;
625 // Can not continue here since the next
626 // enter would result in error anyway
627 goto finish;
628 }
629
630 if (!strcmp(key, "time_mode"))
631 {
632 r = sd_bus_message_read(m, "v", "s", &value);
633 if (r < 0)
634 {
635 std::cerr << "Error: " << strerror(-r)
636 << "reading timeMode" << std::endl;
637 goto finish;
638 }
639 r = tmgr->config.updatePropertyVal(key, value);
640 if (r < 0)
641 {
642 std::cerr << "Error: " << strerror(-r)
643 << "processing timeMode" << std::endl;
644 goto finish;
645 }
646 }
647 else if (!strcmp(key, "time_owner"))
648 {
649 r = sd_bus_message_read(m, "v", "s", &value);
650 if (r < 0)
651 {
652 std::cerr << "Error: " << strerror(-r)
653 << "reading timeOwner" << std::endl;
654 goto finish;
655 }
656 r = tmgr->config.updatePropertyVal(key, value);
657 if (r < 0)
658 {
659 std::cerr << "Error: " << strerror(-r)
660 << "processing time_owner" << std::endl;
661 goto finish;
662 }
663 else if (tmgr->config.isSplitModeChanged())
664 {
665 // Must have been a change away from mode SPLIT
666 tmgr->resetHostOffset();
667 }
668 }
669 else if (!strcmp(key, "use_dhcp_ntp"))
670 {
671 r = sd_bus_message_read(m, "v", "s", &value);
672 if (r < 0)
673 {
674 std::cerr << "Error: " << strerror(-r)
675 <<"reading use_dhcp_ntp" << std::endl;
676 goto finish;
677 }
678 r = tmgr->config.updatePropertyVal(key, value);
679 if (r < 0)
680 {
681 std::cerr << "Error: " << strerror(-r)
682 << "processing dhcp_ntp" << std::endl;
683 goto finish;
684 }
685 }
686 else
687 {
688 sd_bus_message_skip(m, "v");
689 }
690 }
691finish:
692 return r;
693}
694
695// Sets up callback handlers for activities on :
696// 1) user request on SD_BUS
697// 2) Time change
698// 3) Settings change
699// 4) System state change;
700int TimeManager::registerCallbackHandlers()
701{
702 constexpr auto WATCH_SETTING_CHANGE =
703 "type='signal',interface='org.freedesktop.DBus.Properties',"
704 "path='/org/openbmc/settings/host0',member='PropertiesChanged'";
705
706 constexpr auto WATCH_PGOOD_CHANGE =
707 "type='signal',interface='org.freedesktop.DBus.Properties',"
708 "path='/org/openbmc/control/power0',member='PropertiesChanged'";
709
710 // Extract the descriptor out of sd_bus construct.
711 auto sdBusFd = sd_bus_get_fd(iv_TimeBus);
712
713 auto r = sd_event_add_io(iv_Event, &iv_EventSource, sdBusFd, EPOLLIN,
714 processSdBusMessage, this);
715 if (r < 0)
716 {
717 std::cerr << "Error: " << strerror(-r)
718 <<" adding sd_bus_message handler" << std::endl;
719 return r;
720 }
721
722 // Choose the MAX time that is possible to aviod mis fires.
723 itimerspec maxTime {};
724 maxTime.it_value.tv_sec = TIME_T_MAX;
725
726 auto timeFd = timerfd_create(CLOCK_REALTIME, 0);
727 if (timeFd < 0)
728 {
729 std::cerr << "Errorno: " << errno << " creating timerfd" << std::endl;
730 return -1;
731 }
732
733 r = timerfd_settime(timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
734 &maxTime, nullptr);
735 if (r)
736 {
737 std::cerr << "Errorno: " << errno << "Setting timerfd" << std::endl;
738 return -1;
739 }
740
741 // Wake me up *only* if someone SETS the time
742 r = sd_event_add_io(iv_Event, &iv_EventSource, timeFd, EPOLLIN,
743 processTimeChange, this);
744 if (r < 0)
745 {
746 std::cerr << "Error: " << strerror(-r)
747 << "adding time_change handler" << std::endl;
748 return r;
749 }
750
751 // Watch for property changes in settingsd
752 r = sd_bus_add_match(iv_TimeBus, NULL, WATCH_SETTING_CHANGE,
753 processPropertyChange, this);
754 if (r < 0)
755 {
756 std::cerr << "Error: " << strerror(-r)
757 <<" adding property change listener" << std::endl;
758 return r;
759 }
760
761 // Watch for state change. Only reliable one to count on is
762 // state of [pgood]. value of [1] meaning host is powering on / powered
763 // on. [0] means powered off.
764 r = sd_bus_add_match(iv_TimeBus, NULL, WATCH_PGOOD_CHANGE,
765 processPgoodChange, this);
766 if (r < 0)
767 {
768 std::cerr << "Error: " << strerror(-r)
769 << " adding pgood change listener" << std::endl;
770 }
771 return r;
772}
773
774int TimeManager::setupTimeManager()
775{
776 auto r = sd_bus_default_system(&iv_TimeBus);
777 if (r < 0)
778 {
779 std::cerr << "Error" << strerror(-r)
780 <<" connecting to system bus" << std::endl;
781 goto finish;
782 }
783
784 r = sd_bus_add_object_manager(iv_TimeBus, NULL, cv_ObjPath);
785 if (r < 0)
786 {
787 std::cerr << "Error" << strerror(-r)
788 <<" adding object manager" << std::endl;
789 goto finish;
790 }
791
792 std::cout <<"Registering dbus methods" << std::endl;
793 r = sd_bus_add_object_vtable(iv_TimeBus,
794 NULL,
795 cv_ObjPath,
796 cv_BusName,
797 timeServicesVtable,
798 this);
799 if (r < 0)
800 {
801 std::cerr << "Error: " << strerror(-r)
802 <<" adding timer services vtable" << std::endl;
803 goto finish;
804 }
805
806 // create a sd_event object and add handlers
807 r = sd_event_default(&iv_Event);
808 if (r < 0)
809 {
810 std::cerr << "Error: " << strerror(-r)
811 <<"creating an sd_event" << std::endl;
812 goto finish;
813 }
814
815 // Handlers called by sd_event when an activity
816 // is observed in event loop
817 r = registerCallbackHandlers();
818 if (r < 0)
819 {
820 std::cerr << "Error setting up callback handlers" << std::endl;
821 goto finish;
822 }
823
824 // Need to do this here since TimeConfig may update the necessary owners
825 r = config.processInitialSettings(iv_TimeBus);
826 if (r < 0)
827 {
828 std::cerr << "Error setting up configuration params" << std::endl;
829 goto finish;
830 }
831
832 // Read saved values from previous run
833 r = readPersistentData();
834 if (r < 0)
835 {
836 std::cerr << "Error reading persistent data" << std::endl;
837 goto finish;
838 }
839
840 // Claim the bus
841 r = sd_bus_request_name(iv_TimeBus, cv_BusName, 0);
842 if (r < 0)
843 {
844 std::cerr << "Error: " << strerror(-r)
845 << "acquiring service name" << std::endl;
846 goto finish;
847 }
848finish:
849 if (r < 0)
850 {
851 iv_EventSource = sd_event_source_unref(iv_EventSource);
852 iv_Event = sd_event_unref(iv_Event);
853 }
854 return r;
855}
856
857int TimeManager::readPersistentData()
858{
859 using namespace std::chrono;
860
861 // Get current host_offset
862 // When we reach here, TimeConfig would have been populated and would have
863 // applied the owner to SPLIT *if* the system allowed it. So check if we
864 // moved away from SPLIT and if so, make offset:0
865 if (config.isSplitModeChanged())
866 {
867 iv_HostOffset = microseconds(0);
868 auto r = config.writeData<decltype(iv_HostOffset.count())>
869 (cv_HostOffsetFile,
870 iv_HostOffset.count());
871 if (r < 0)
872 {
873 std::cerr <<" Error saving offset to file" << std::endl;
874 return r;
875 }
876 }
877 else
878 {
879 auto hostTimeOffset = config.readData<long long int>(cv_HostOffsetFile);
880 iv_HostOffset = microseconds(hostTimeOffset);
881 std::cout <<"Last known host_offset:" << hostTimeOffset << std::endl;
882 }
883
884 //How long was the FSP up prior to 'this' start
885 iv_UptimeUsec = duration_cast<microseconds>
886 (system_clock::now().time_since_epoch() -
887 steady_clock::now().time_since_epoch());
888 if (iv_UptimeUsec.count() < 0)
889 {
890 std::cerr <<"Error reading uptime" << std::endl;
891 return -1;
892 }
893 std::cout <<"Initial Uptime Usec: "
894 << iv_UptimeUsec.count() << std::endl;
895 return 0;
896}
897
898// Forever loop
899int TimeManager::waitForClientRequest()
900{
901 return sd_event_loop(iv_Event);
902}
903
904int main(int argc, char* argv[])
905{
906 auto tmgr = std::make_unique<TimeManager>();
907
908 // Wait for the work
909 auto r = tmgr->waitForClientRequest();
910
911 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
912}