1 #include "manager.hpp"
2 
3 #include "utils.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 #include <cassert>
8 
9 namespace rules = sdbusplus::bus::match::rules;
10 
11 namespace // anonymous
12 {
13 
14 constexpr auto systemdTimeService = "org.freedesktop.timedate1";
15 constexpr auto systemdTimePath = "/org/freedesktop/timedate1";
16 constexpr auto systemdTimeInterface = "org.freedesktop.timedate1";
17 constexpr auto methodSetNtp = "SetNTP";
18 constexpr auto propertyNtp = "NTP";
19 } // namespace
20 
21 namespace phosphor
22 {
23 namespace time
24 {
25 
26 PHOSPHOR_LOG2_USING;
27 
Manager(sdbusplus::bus_t & bus)28 Manager::Manager(sdbusplus::bus_t& bus) : bus(bus), settings(bus)
29 {
30     using namespace sdbusplus::bus::match::rules;
31     timedateMatches.emplace_back(
32         bus, propertiesChanged(systemdTimePath, systemdTimeInterface),
33         [&](sdbusplus::message_t& m) { onTimedateChanged(m); });
34     settingsMatches.emplace_back(
35         bus, propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf),
36         [&](sdbusplus::message_t& m) { onSettingsChanged(m); });
37 
38     // Check the settings daemon to process the new settings
39     auto mode = getSetting(settings.timeSyncMethod.c_str(),
40                            settings::timeSyncIntf, propertyTimeMode);
41 
42     onPropertyChanged(propertyTimeMode, mode, true);
43 }
44 
onPropertyChanged(const std::string & key,const std::string & value,bool forceSet)45 void Manager::onPropertyChanged(const std::string& key,
46                                 const std::string& value, bool forceSet)
47 {
48     assert(key == propertyTimeMode);
49 
50     bool newNtpMode = (settings::ntpSync == value);
51     bool oldNtpMode = (Mode::NTP == getTimeMode());
52     if (forceSet || (newNtpMode != oldNtpMode))
53     {
54         // Notify listeners
55         onTimeModeChanged(value);
56         setCurrentTimeMode(value);
57         debug("NTP property changed in phosphor-settings, update to systemd"
58               " time service.");
59     }
60     else
61     {
62         debug("NTP mode is already the same, skip setting to systemd time"
63               " service again.");
64     }
65 }
66 
onSettingsChanged(sdbusplus::message_t & msg)67 int Manager::onSettingsChanged(sdbusplus::message_t& msg)
68 {
69     using Interface = std::string;
70     using Property = std::string;
71     using Value = std::string;
72     using Properties = std::map<Property, std::variant<Value>>;
73 
74     Interface interface;
75     Properties properties;
76 
77     msg.read(interface, properties);
78 
79     for (const auto& p : properties)
80     {
81         onPropertyChanged(p.first, std::get<std::string>(p.second));
82     }
83 
84     return 0;
85 }
86 
onTimedateChanged(sdbusplus::message_t & msg)87 int Manager::onTimedateChanged(sdbusplus::message_t& msg)
88 {
89     using Properties = std::map<std::string, std::variant<std::string, bool>>;
90 
91     std::string interface;
92     Properties properties;
93 
94     msg.read(interface, properties);
95 
96     auto iter = properties.find(propertyNtp);
97     if (iter == properties.end())
98     {
99         return -1;
100     }
101 
102     try
103     {
104         bool newNtpMode = std::get<bool>(iter->second);
105         bool oldNtpMode = (Mode::NTP == getTimeMode());
106         if (newNtpMode != oldNtpMode)
107         {
108             const auto& timeMode =
109                 newNtpMode ? settings::ntpSync : settings::manualSync;
110             std::string settingManager = utils::getService(
111                 bus, settings.timeSyncMethod.c_str(), settings::timeSyncIntf);
112             utils::setProperty(bus, settingManager, settings.timeSyncMethod,
113                                settings::timeSyncIntf, propertyTimeMode,
114                                timeMode);
115             setCurrentTimeMode(timeMode);
116             debug("NTP property changed in systemd time service, update to"
117                   " phosphor-settings.");
118         }
119         else
120         {
121             debug("NTP mode is already the same, skip setting to"
122                   " phosphor-settings again.");
123         }
124     }
125     catch (const std::exception& ex)
126     {
127         error("Failed to sync NTP: {ERROR}", "ERROR", ex);
128     }
129 
130     return 0;
131 }
132 
updateNtpSetting(const std::string & value)133 void Manager::updateNtpSetting(const std::string& value)
134 {
135     try
136     {
137         bool isNtp =
138             (value == "xyz.openbmc_project.Time.Synchronization.Method.NTP");
139         auto method = bus.new_method_call(systemdTimeService, systemdTimePath,
140                                           systemdTimeInterface, methodSetNtp);
141         method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable
142                                      // 'false' meaning no policy-kit
143 
144         bus.call_noreply(method);
145         info("Updated NTP setting: {ENABLED}", "ENABLED", isNtp);
146     }
147     catch (const sdbusplus::exception_t& ex)
148     {
149         error("Failed to update NTP setting: {ERROR}", "ERROR", ex);
150     }
151 }
152 
setCurrentTimeMode(const std::string & mode)153 bool Manager::setCurrentTimeMode(const std::string& mode)
154 {
155     try
156     {
157         auto newMode = utils::strToMode(mode);
158         if (newMode != timeMode)
159         {
160             info("Time mode has been changed to {MODE}", "MODE", newMode);
161             timeMode = newMode;
162             return true;
163         }
164     }
165     catch (const sdbusplus::exception_t& ex)
166     {
167         error("Failed to convert mode from string: {ERROR}", "ERROR", ex);
168     }
169 
170     return false;
171 }
172 
onTimeModeChanged(const std::string & mode)173 void Manager::onTimeModeChanged(const std::string& mode)
174 {
175     // When time_mode is updated, update the NTP setting
176     updateNtpSetting(mode);
177 }
178 
getSetting(const char * path,const char * interface,const char * setting) const179 std::string Manager::getSetting(const char* path, const char* interface,
180                                 const char* setting) const
181 {
182     try
183     {
184         std::string settingManager = utils::getService(bus, path, interface);
185         return utils::getProperty<std::string>(bus, settingManager.c_str(),
186                                                path, interface, setting);
187     }
188     catch (const std::exception& ex)
189     {
190         error(
191             "Failed to get property: {ERROR}, path: {PATH}, interface: {INTERFACE}, name: {NAME}",
192             "ERROR", ex, "PATH", path, "INTERFACE", interface, "NAME", setting);
193         return {};
194     }
195 }
196 
197 } // namespace time
198 } // namespace phosphor
199