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