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