blob: da54581137143bb9aab4a6187225730b5228bcee [file] [log] [blame]
Vishwanatha81ee91f2016-08-30 17:17:13 +05301#include <cstdlib>
2#include <fstream>
3#include <iostream>
4#include <memory>
5#include <mapper.h>
6#include "time-manager.hpp"
7
8std::map<std::string, TimeConfig::FUNCTOR> TimeConfig::iv_TimeParams = {
9 { "time_mode", std::make_tuple(&TimeConfig::getSystemSettings,
10 &TimeConfig::updateTimeMode)
11 },
12
13 { "time_owner", std::make_tuple(&TimeConfig::getSystemSettings,
14 &TimeConfig::updateTimeOwner)
15 },
16
17 { "use_dhcp_ntp", std::make_tuple(&TimeConfig::getSystemSettings,
18 &TimeConfig::updateNetworkSettings)
19 }
20};
21
22TimeConfig::TimeConfig() :
Patrick Williams6a07ddb2016-11-09 21:43:20 -060023 iv_dbus(nullptr),
Vishwanatha81ee91f2016-08-30 17:17:13 +053024 iv_CurrTimeMode(timeModes::NTP),
25 iv_RequestedTimeMode(timeModes::NTP),
26 iv_CurrTimeOwner(timeOwners::BMC),
27 iv_RequestedTimeOwner(timeOwners::BMC),
28 iv_CurrDhcpNtp("yes"),
29 iv_SettingChangeAllowed(false),
Patrick Williams6a07ddb2016-11-09 21:43:20 -060030 iv_SplitModeChanged(false)
Vishwanatha81ee91f2016-08-30 17:17:13 +053031{
32 // Not really having anything to do here.
33}
34
35// Given a mode string, returns it's equivalent mode enum
36TimeConfig::timeModes TimeConfig::getTimeMode(const char* timeMode)
37{
38 // We are forcing the values to be in specific case and range
39 if (!strcmp(timeMode,"NTP"))
40 {
41 return timeModes::NTP;
42 }
43 else
44 {
45 return timeModes::MANUAL;
46 }
47}
48
49// Accepts a timeMode enum and returns it's string value
50const char* TimeConfig::modeStr(const TimeConfig::timeModes timeMode)
51{
52 switch(timeMode)
53 {
54 case timeModes::NTP:
55 {
56 return "NTP";
57 }
58 case timeModes::MANUAL:
59 {
60 return "MANUAL";
61 }
62 }
63}
64
65// Given a owner string, returns it's equivalent owner enum
66TimeConfig::timeOwners TimeConfig::getTimeOwner(const char* timeOwner)
67{
68 if (!strcmp(timeOwner,"BMC"))
69 {
70 return timeOwners::BMC;
71 }
72 else if (!strcmp(timeOwner,"HOST"))
73 {
74 return timeOwners::HOST;
75 }
76 else if (!strcmp(timeOwner,"SPLIT"))
77 {
78 return timeOwners::SPLIT;
79 }
80 else
81 {
82 return timeOwners::BOTH;
83 }
84}
85
86// Accepts a timeOwner enum and returns it's string value
87const char* TimeConfig::ownerStr(const timeOwners timeOwner)
88{
89 switch(timeOwner)
90 {
91 case timeOwners::BMC:
92 {
93 return "BMC";
94 }
95 case timeOwners::HOST:
96 {
97 return "HOST";
98 }
99 case timeOwners::SPLIT:
100 {
101 return "SPLIT";
102 }
103 case timeOwners::BOTH:
104 {
105 return "BOTH";
106 }
107 }
108}
109
110// Returns the busname that hosts objPath
111std::unique_ptr<char> TimeConfig::getProvider(const char* objPath)
112{
113 char *provider = nullptr;
114 mapper_get_service(iv_dbus, objPath, &provider);
115 return std::unique_ptr<char>(provider);
116}
117
118// Accepts a settings name and returns its value.
119// for the variant of type 'string' now.
120std::string TimeConfig::getSystemSettings(const char* key)
121{
122 constexpr auto settingsObj = "/org/openbmc/settings/host0";
123 constexpr auto propertyIntf = "org.freedesktop.DBus.Properties";
124 constexpr auto hostIntf = "org.openbmc.settings.Host";
125
126 const char* value = nullptr;
127 std::string settingsVal {};
128 sd_bus_message* reply = nullptr;
129
130 std::cout <<"Getting System Settings: " << key << std::endl;
131
132 // Get the provider from object mapper
133 auto settingsProvider = getProvider(settingsObj);
134 if (!settingsProvider)
135 {
136 std::cerr << "Error Getting service for Settings" << std::endl;
137 return value;
138 }
139
140 auto r = sd_bus_call_method(iv_dbus,
141 settingsProvider.get(),
142 settingsObj,
143 propertyIntf,
144 "Get",
145 nullptr,
146 &reply,
147 "ss",
148 hostIntf,
149 key);
150 if (r < 0)
151 {
152 std::cerr <<"Error" << strerror(-r)
153 <<" reading system settings" << std::endl;
154 goto finish;
155 }
156
157 r = sd_bus_message_read(reply, "v", "s", &value);
158 if (r < 0)
159 {
160 std::cerr <<"Error " << strerror(-r)
161 <<" parsing settings data" << std::endl;
162 }
163finish:
164 if (value)
165 {
166 settingsVal.assign(value);
167 }
168 reply = sd_bus_message_unref(reply);
169 return settingsVal;
170}
171
172// Reads value from /org/openbmc/control/power0
173// This signature on purpose to plug into time parameter map
174std::string TimeConfig::getPowerSetting(const char* key)
175{
176 constexpr auto powerObj = "/org/openbmc/control/power0";
177 constexpr auto powerIntf = "org.openbmc.control.Power";
178 constexpr auto propertyIntf = "org.freedesktop.DBus.Properties";
179
180 int value = -1;
181 std::string powerValue {};
182 sd_bus_message* reply = nullptr;
183
184 std::cout <<"Reading Power Control key: " << key << std::endl;
185
186 // Get the provider from object mapper
187 auto powerProvider = getProvider(powerObj);
188 if (!powerProvider)
189 {
190 std::cerr <<" Error getting provider for Power Settings" << std::endl;
191 return powerValue;
192 }
193
194 auto r = sd_bus_call_method(iv_dbus,
195 powerProvider.get(),
196 powerObj,
197 propertyIntf,
198 "Get",
199 nullptr,
200 &reply,
201 "ss",
202 powerIntf,
203 key);
204 if (r < 0)
205 {
206 std::cerr <<"Error " << strerror(-r)
207 << "reading: " << key << std::endl;
208 goto finish;
209 }
210
211 r = sd_bus_message_read(reply, "v", "i", &value);
212 if (r < 0)
213 {
214 std::cerr <<"Error " << strerror(-r)
215 <<" parsing " << key << "value" << std::endl;
216 // For maintenance
217 goto finish;
218 }
219finish:
220 if (value != -1)
221 {
222 powerValue = std::to_string(value);
223 }
224 reply = sd_bus_message_unref(reply);
225 return powerValue;
226}
227
228// Updates .network file with UseNtp=
229int TimeConfig::updateNetworkSettings(const std::string& useDhcpNtp)
230{
231 constexpr auto networkObj = "/org/openbmc/NetworkManager/Interface";
232 constexpr auto networkIntf = "org.openbmc.NetworkManager";
233
234 std::cout << "use_dhcp_ntp = " << useDhcpNtp.c_str() << std::endl;
235
236 // If what we have already is what it is, then just return.
237 if (iv_CurrDhcpNtp == useDhcpNtp)
238 {
239 return 0;
240 }
241
242 // Get the provider from object mapper
243 auto networkProvider = getProvider(networkObj);
244 if (!networkProvider)
245 {
246 return -1;
247 }
248
249 auto r = sd_bus_call_method(iv_dbus,
250 networkProvider.get(),
251 networkObj,
252 networkIntf,
253 "UpdateUseNtpField",
254 nullptr,
255 nullptr,
256 "s",
257 useDhcpNtp.c_str());
258 if (r < 0)
259 {
260 std::cerr <<"Error " << strerror(-r)
261 << " updating UseNtp" << std::endl;
262 }
263 else
264 {
265 std::cout <<"Successfully updated UseNtp=["
266 << useDhcpNtp << "]" << std::endl;
267
Patrick Williamsf0c91c02016-11-09 21:35:04 -0600268 r = writeData(cv_DhcpNtpFile, useDhcpNtp);
Vishwanatha81ee91f2016-08-30 17:17:13 +0530269 }
270
271 return 0;
272}
273
274// Reads the values from 'settingsd' and applies:
275// 1) Time Mode
276// 2) time Owner
277// 3) UseNTP setting
278// 4) Pgood
279int TimeConfig::processInitialSettings(sd_bus* dbus)
280{
281 // First call from TimeManager to config manager
282 iv_dbus = dbus;
283
284 // Read saved info like Who was the owner , what was the mode,
285 // what was the use_dhcp_ntp setting before etc..
286 auto r = readPersistentData();
287 if (r < 0)
288 {
289 std::cerr << "Error reading the data saved in flash."
290 << std::endl;
291 return r;
292 }
293
294 // Now read whats in settings and apply if allowed.
295 for (auto& iter : iv_TimeParams)
296 {
297 // Get the settings value for various keys.
298 auto reader = std::get<READER>(iter.second);
299 auto value = (this->*reader)(iter.first.c_str());
300 if (!value.empty())
301 {
302 // Get the value for the key and validate.
303 auto updater = std::get<UPDATER>(iter.second);
304 auto r = (this->*updater)(value);
305 if (r < 0)
306 {
307 std::cerr << "Error setting up initial keys" << std::endl;
308 return r;
309 }
310 }
311 else
312 {
313 std::cerr << "key " << iter.first
314 <<" has no value: " << std::endl;
315 return -1;
316 }
317 }
318
319 // Now that we have taken care of consuming, push this as well
320 // so that we can use the same map for handling pgood change too.
321 auto readerUpdater = std::make_tuple(&TimeConfig::getPowerSetting,
322 &TimeConfig::processPgoodChange);
323 iv_TimeParams.emplace("pgood", readerUpdater);
324
325 return 0;
326}
327
328// This is called by Property Change handler on the event of
329// receiving notification on property value change.
330int TimeConfig::updatePropertyVal(const char* key, const std::string& value)
331{
332 auto iter = iv_TimeParams.find(key);
333 if (iter != iv_TimeParams.end())
334 {
335 auto updater = std::get<UPDATER>(iter->second);
336 return (this->*updater)(value);
337 }
338 // Coming here indicates that we never had a matching key.
339 return -1;
340}
341
342// Called by sd_event when Properties are changed in Control/power0
343// Interested in change to 'pgood'
344int TimeConfig::processPgoodChange(const std::string& newPgood)
345{
346 // Indicating that we are safe to apply any changes
347 if (!newPgood.compare("0"))
348 {
349 iv_SettingChangeAllowed = true;
350 std::cout <<"Changing time settings allowed now" << std::endl;
351 }
352 else
353 {
354 iv_SettingChangeAllowed = false;
355 std::cout <<"Changing time settings is *deferred* now" << std::endl;
356 }
357
358 // if we have had users that changed the time settings
359 // when we were not ready yet, do it now.
360 if (iv_RequestedTimeOwner != iv_CurrTimeOwner)
361 {
362 auto r = updateTimeOwner(ownerStr(iv_RequestedTimeOwner));
363 if (r < 0)
364 {
365 std::cerr << "Error updating new time owner" << std::endl;
366 return r;
367 }
368 std::cout << "New Owner is : "
369 << ownerStr(iv_RequestedTimeOwner) << std::endl;
370 }
371
372 if (iv_RequestedTimeMode != iv_CurrTimeMode)
373 {
374 auto r = updateTimeMode(modeStr(iv_RequestedTimeMode));
375 if (r < 0)
376 {
377 std::cerr << "Error updating new time mode" << std::endl;
378 return r;
379 }
380 std::cout <<"New Mode is : "
381 << modeStr(iv_RequestedTimeMode) << std::endl;
382 }
383 return 0;
384}
385
386// Manipulates time owner if the system setting allows it
387int TimeConfig::updateTimeMode(const std::string& newModeStr)
388{
389 auto r = 0;
390 iv_RequestedTimeMode = getTimeMode(newModeStr.c_str());
391
392 std::cout <<"Requested_Mode: " << newModeStr
393 << " Current_Mode: " << modeStr(iv_CurrTimeMode)
394 << std::endl;
395
396 if (iv_RequestedTimeMode == iv_CurrTimeMode)
397 {
398 std::cout << "Mode is already set to : "
399 << newModeStr << std::endl;
400 return 0;
401 }
402
403 // Also, if the time owner is HOST, then we should not allow NTP.
404 // However, it may so happen that there are 2 pending requests, one for
405 // changing to NTP and other for changing owner to something not HOST.
406 // So check if there is a pending timeOwner change and if so, allow NTP
407 // if the current is HOST and requested is non HOST.
408 if (iv_CurrTimeOwner == timeOwners::HOST &&
409 iv_RequestedTimeOwner == timeOwners::HOST &&
410 iv_RequestedTimeMode == timeModes::NTP)
411 {
412 std::cout <<"Can not set mode to NTP with HOST as owner"
413 << std::endl;
414 return 0;
415 }
416
417 if (iv_SettingChangeAllowed)
418 {
419 r = modifyNtpSettings(iv_RequestedTimeMode);
420 if (r < 0)
421 {
422 std::cerr << "Error changing TimeMode settings"
423 << std::endl;
424 }
425 else
426 {
427 iv_CurrTimeMode = iv_RequestedTimeMode;
428 }
429 std::cout << "Current_Mode changed to: "
430 << newModeStr << " :: " << modeStr(iv_CurrTimeMode) << std::endl;
431
432 // Need this when we either restart or come back from reset
433 r = writeData(cv_TimeModeFile, modeStr(iv_CurrTimeMode));
434 }
435 else
436 {
437 std::cout <<"Deferring update until system state allows it"
438 << std::endl;
439 }
440 return r;
441}
442
443// Manipulates time owner if the system setting allows it
444int TimeConfig::updateTimeOwner(const std::string& newOwnerStr)
445{
446 int r = 0;
447 iv_RequestedTimeOwner = getTimeOwner(newOwnerStr.c_str());
448
449 // Needed when owner changes to HOST
450 std::string manualMode = "Manual";
451
452 // Needed by time manager to do some house keeping
453 iv_SplitModeChanged = false;
454
455 if (iv_RequestedTimeOwner == iv_CurrTimeOwner)
456 {
457 std::cout <<"Owner is already set to : "
458 << newOwnerStr << std::endl;
459 return 0;
460 }
461
462 std::cout <<"Requested_Owner: " << newOwnerStr
463 << " Current_Owner: " << ownerStr(iv_CurrTimeOwner)
464 << std::endl;
465
466 if (iv_SettingChangeAllowed)
467 {
468 // If we are transitioning from SPLIT to something else,
469 // reset the host offset.
470 if (iv_CurrTimeOwner == timeOwners::SPLIT &&
471 iv_RequestedTimeOwner != timeOwners::SPLIT)
472 {
473 // Needed by time manager to do some house keeping
474 iv_SplitModeChanged = true;
475 }
476 iv_CurrTimeOwner = iv_RequestedTimeOwner;
477 std::cout << "Current_Owner is now: "
478 << newOwnerStr << std::endl;
479
480 // HOST and NTP are exclusive
481 if (iv_CurrTimeOwner == timeOwners::HOST)
482 {
483 std::cout <<"Forcing the mode to MANUAL" << std::endl;
484 r = updateTimeMode(manualMode);
485 if (r < 0)
486 {
487 std::cerr << "Error forcing the mode to MANUAL" << std::endl;
488 return r;
489 }
490 }
491 // Need this when we either restart or come back from reset
492 r = writeData(cv_TimeOwnerFile, ownerStr(iv_CurrTimeOwner));
493 }
494 else
495 {
496 std::cout <<"Deferring update until system state allows it"
497 << std::endl;
498 }
499
500 return r;
501}
502
503// Accepts the time mode and makes necessary changes to timedate1
504int TimeConfig::modifyNtpSettings(const timeModes& newTimeMode)
505{
506 auto ntpChangeOp = 0;
507
508 // Pass '1' -or- '0' to SetNTP method indicating Enable/Disable
509 ntpChangeOp = (newTimeMode == timeModes::NTP) ? 1 : 0;
510
511 std::cout <<"Applying NTP setting..." << std::endl;
512
513 auto r = sd_bus_call_method(iv_dbus,
514 "org.freedesktop.timedate1",
515 "/org/freedesktop/timedate1",
516 "org.freedesktop.timedate1",
517 "SetNTP",
518 nullptr,
519 nullptr, // timedate1 does not return response
520 "bb",
521 ntpChangeOp, // '1' means Enable
522 0); // '0' meaning no policy-kit
523 if (r < 0)
524 {
525 std::cerr <<"Error: " << strerror(-r)
526 << "changing time Mode" << std::endl;
527 }
528 else
529 {
530 std::cout << "SUCCESS. NTP setting is now: " <<
531 (ntpChangeOp) ? "Enabled" : "Disabled";
532
533 // TODO : https://github.com/openbmc/phosphor-time-manager/issues/1
534 if (ntpChangeOp)
535 {
Patrick Williamsd5ba0082016-11-09 21:36:21 -0600536 r = system("systemctl restart systemd-timesyncd &");
Vishwanatha81ee91f2016-08-30 17:17:13 +0530537 }
538 else
539 {
Patrick Williamsd5ba0082016-11-09 21:36:21 -0600540 r = system("systemctl stop systemd-timesyncd &");
Vishwanatha81ee91f2016-08-30 17:17:13 +0530541 }
542 }
543 return r;
544}
545
546// Reads all the saved data from the last run
547int TimeConfig::readPersistentData()
548{
549 // If we are coming back from a reset reload, then need to
550 // read what was the last successful Mode and Owner.
551 auto savedTimeMode = readData<std::string>(cv_TimeModeFile);
552 if (!savedTimeMode.empty())
553 {
554 iv_CurrTimeMode = getTimeMode(savedTimeMode.c_str());
555 std::cout <<"Last known time_mode: "
556 << savedTimeMode.c_str() << std::endl;
557 }
558
559 auto savedTimeOwner = readData<std::string>(cv_TimeOwnerFile);
560 if (!savedTimeOwner.empty())
561 {
562 iv_CurrTimeOwner = getTimeOwner(savedTimeOwner.c_str());
563 std::cout <<"Last known time_owner: "
564 << savedTimeOwner.c_str() << std::endl;
565 }
566
567 auto savedDhcpNtp = readData<std::string>(cv_DhcpNtpFile);
568 if (!savedDhcpNtp.empty())
569 {
570 iv_CurrDhcpNtp = savedDhcpNtp;
571 std::cout <<"Last known use_dhcp_ntp: "
572 << iv_CurrDhcpNtp.c_str() << std::endl;
573 }
574 else
575 {
576 // This seems to be the first time.
577 std::cerr <<"Empty DhcpNtp string" << std::endl;
578 iv_CurrDhcpNtp = "yes";
579 }
580
581 // Doing this here to make sure 'pgood' is read and handled
582 // first before anything.
583 auto pgood = getPowerSetting("pgood");
584 if (!pgood.compare("0"))
585 {
586 std::cout << "Changing settings *allowed* now" << std::endl;
587 iv_SettingChangeAllowed = true;
588 }
589 return 0;
590}