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