1 #include "manager.hpp"
2 #include "utils.hpp"
3 
4 #include <phosphor-logging/elog.hpp>
5 #include <phosphor-logging/elog-errors.hpp>
6 #include <phosphor-logging/log.hpp>
7 #include <xyz/openbmc_project/Common/error.hpp>
8 
9 namespace rules = sdbusplus::bus::match::rules;
10 
11 namespace // anonymous
12 {
13 constexpr auto SETTINGS_PATH = "/org/openbmc/settings/host0";
14 constexpr auto SETTINGS_INTERFACE = "org.openbmc.settings.Host";
15 
16 // TODO: Use new settings in xyz.openbmc_project
17 const auto MATCH_PROPERTY_CHANGE =
18     rules::type::signal() +
19     rules::member("PropertiesChanged") +
20     rules::path("/org/openbmc/settings/host0") +
21     rules::interface("org.freedesktop.DBus.Properties");
22 
23 const auto MATCH_PGOOD_CHANGE =
24     rules::type::signal() +
25     rules::member("PropertiesChanged") +
26     rules::path("/org/openbmc/control/power0") +
27     rules::interface("org.freedesktop.DBus.Properties");
28 
29 constexpr auto POWER_PATH = "/org/openbmc/control/power0";
30 constexpr auto POWER_INTERFACE = "org.openbmc.control.Power";
31 constexpr auto PGOOD_STR = "pgood";
32 
33 constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1";
34 constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1";
35 constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1";
36 constexpr auto METHOD_SET_NTP = "SetNTP";
37 
38 constexpr auto OBMC_NETWORK_PATH = "/org/openbmc/NetworkManager/Interface";
39 constexpr auto OBMC_NETWORK_INTERFACE = "org.openbmc.NetworkManager";
40 constexpr auto METHOD_UPDATE_USE_NTP = "UpdateUseNtpField";
41 }
42 
43 namespace phosphor
44 {
45 namespace time
46 {
47 
48 using namespace phosphor::logging;
49 
50 const std::set<std::string>
51 Manager::managedProperties = {PROPERTY_TIME_MODE, PROPERTY_TIME_OWNER};
52 
53 Manager::Manager(sdbusplus::bus::bus& bus)
54     : bus(bus),
55       propertyChangeMatch(bus, MATCH_PROPERTY_CHANGE, onPropertyChanged, this),
56       pgoodChangeMatch(bus, MATCH_PGOOD_CHANGE, onPgoodChanged, this)
57 {
58     using namespace sdbusplus::bus::match::rules;
59     settingsMatches.emplace_back(
60         bus,
61         propertiesChanged(settings.timeOwner, settings::timeOwnerIntf),
62         std::bind(std::mem_fn(&Manager::onSettingsChanged),
63                   this, std::placeholders::_1));
64     settingsMatches.emplace_back(
65         bus,
66         propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf),
67         std::bind(std::mem_fn(&Manager::onSettingsChanged),
68           this, std::placeholders::_1));
69 
70     checkHostOn();
71 
72     // Restore settings from persistent storage
73     restoreSettings();
74 
75     // Check the settings daemon to process the new settings
76     auto mode = getSetting(settings.timeSyncMethod.c_str(),
77                            settings::timeSyncIntf,
78                            PROPERTY_TIME_MODE);
79     auto owner = getSetting(settings.timeOwner.c_str(),
80                             settings::timeOwnerIntf,
81                             PROPERTY_TIME_OWNER);
82 
83     onPropertyChanged(PROPERTY_TIME_MODE, mode);
84     onPropertyChanged(PROPERTY_TIME_OWNER, owner);
85 
86     checkDhcpNtp();
87 }
88 
89 void Manager::addListener(PropertyChangeListner* listener)
90 {
91     // Notify listener about the initial value
92     listener->onModeChanged(timeMode);
93     listener->onOwnerChanged(timeOwner);
94 
95     listeners.insert(listener);
96 }
97 
98 void Manager::restoreSettings()
99 {
100     auto mode = utils::readData<std::string>(modeFile);
101     if (!mode.empty())
102     {
103         timeMode = utils::strToMode(mode);
104     }
105     auto owner = utils::readData<std::string>(ownerFile);
106     if (!owner.empty())
107     {
108         timeOwner = utils::strToOwner(owner);
109     }
110 }
111 
112 void Manager::checkHostOn()
113 {
114     std::string powerService = utils::getService(bus,
115                                                  POWER_PATH,
116                                                  POWER_INTERFACE);
117 
118     int pgood = utils::getProperty<int>(bus,
119                                         powerService.c_str(),
120                                         POWER_PATH,
121                                         POWER_INTERFACE,
122                                         PGOOD_STR);
123     hostOn = static_cast<bool>(pgood);
124 }
125 
126 void Manager::checkDhcpNtp()
127 {
128     std::string useDhcpNtp = getSettings(PROPERTY_DHCP_NTP);
129     updateDhcpNtpSetting(useDhcpNtp);
130 }
131 
132 void Manager::onPropertyChanged(const std::string& key,
133                                 const std::string& value)
134 {
135     if (hostOn)
136     {
137         // If host is on, set the values as requested time mode/owner.
138         // And when host becomes off, notify the listners.
139         setPropertyAsRequested(key, value);
140     }
141     else
142     {
143         // If host is off, notify listners
144         if (key == PROPERTY_TIME_MODE)
145         {
146             setCurrentTimeMode(value);
147             onTimeModeChanged(value);
148         }
149         else if (key == PROPERTY_TIME_OWNER)
150         {
151             setCurrentTimeOwner(value);
152             onTimeOwnerChanged();
153         }
154     }
155 }
156 
157 int Manager::onPropertyChanged(sd_bus_message* msg,
158                                void* userData,
159                                sd_bus_error* retError)
160 {
161     using properties = std::map < std::string,
162           sdbusplus::message::variant<std::string> >;
163     auto m = sdbusplus::message::message(msg);
164     // message type: sa{sv}as
165     std::string ignore;
166     properties props;
167     m.read(ignore, props);
168     auto manager = static_cast<Manager*>(userData);
169     for (const auto& item : props)
170     {
171         if (managedProperties.find(item.first) != managedProperties.end())
172         {
173             // For managed properties, notify listeners
174             manager->onPropertyChanged(
175                 item.first, item.second.get<std::string>());
176         }
177         else if (item.first == PROPERTY_DHCP_NTP)
178         {
179             // For other manager interested properties, handle specifically
180             manager->updateDhcpNtpSetting(item.second.get<std::string>());
181         }
182     }
183     return 0;
184 }
185 
186 int Manager::onSettingsChanged(sdbusplus::message::message& msg)
187 {
188     using Interface = std::string;
189     using Property = std::string;
190     using Value = std::string;
191     using Properties = std::map<Property, sdbusplus::message::variant<Value>>;
192 
193     Interface interface;
194     Properties properties;
195 
196     msg.read(interface, properties);
197 
198     for(const auto& p : properties)
199     {
200         onPropertyChanged(p.first, p.second.get<std::string>());
201     }
202 
203     return 0;
204 }
205 
206 void Manager::setPropertyAsRequested(const std::string& key,
207                                      const std::string& value)
208 {
209     if (key == PROPERTY_TIME_MODE)
210     {
211         setRequestedMode(value);
212     }
213     else if (key == PROPERTY_TIME_OWNER)
214     {
215         setRequestedOwner(value);
216     }
217     else
218     {
219         // The key shall be already the supported one
220         using InvalidArgumentError =
221             sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
222         using namespace xyz::openbmc_project::Common;
223         elog<InvalidArgumentError>(
224             InvalidArgument::ARGUMENT_NAME(key.c_str()),
225             InvalidArgument::ARGUMENT_VALUE(value.c_str()));
226     }
227 }
228 
229 void Manager::setRequestedMode(const std::string& mode)
230 {
231     requestedMode = mode;
232 }
233 
234 void Manager::setRequestedOwner(const std::string& owner)
235 {
236     requestedOwner = owner;
237 }
238 
239 void Manager::updateNtpSetting(const std::string& value)
240 {
241     bool isNtp =
242         (value == "xyz.openbmc_project.Time.Synchronization.Method.NTP");
243     auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE,
244                                       SYSTEMD_TIME_PATH,
245                                       SYSTEMD_TIME_INTERFACE,
246                                       METHOD_SET_NTP);
247     method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable
248                                  // 'false' meaning no policy-kit
249 
250     if (bus.call(method))
251     {
252         log<level::INFO>("Updated NTP setting",
253                          entry("ENABLED:%d", isNtp));
254     }
255     else
256     {
257         log<level::ERR>("Failed to update NTP setting");
258     }
259 }
260 
261 void Manager::updateDhcpNtpSetting(const std::string& useDhcpNtp)
262 {
263     std::string networkService = utils::getService(bus,
264                                                    OBMC_NETWORK_PATH,
265                                                    OBMC_NETWORK_INTERFACE);
266 
267     auto method = bus.new_method_call(networkService.c_str(),
268                                       OBMC_NETWORK_PATH,
269                                       OBMC_NETWORK_INTERFACE,
270                                       METHOD_UPDATE_USE_NTP);
271     method.append(useDhcpNtp);
272 
273     if (bus.call(method))
274     {
275         log<level::INFO>("Updated use ntp field",
276                          entry("USENTPFIELD:%s", useDhcpNtp.c_str()));
277     }
278     else
279     {
280         log<level::ERR>("Failed to update UseNtpField");
281     }
282 }
283 
284 void Manager::onPgoodChanged(bool pgood)
285 {
286     hostOn = pgood;
287     if (hostOn)
288     {
289         return;
290     }
291     if (!requestedMode.empty())
292     {
293         if (setCurrentTimeMode(requestedMode))
294         {
295             onTimeModeChanged(requestedMode);
296         }
297         setRequestedMode({}); // Clear requested mode
298     }
299     if (!requestedOwner.empty())
300     {
301         if (setCurrentTimeOwner(requestedOwner))
302         {
303             onTimeOwnerChanged();
304         }
305         setRequestedOwner({}); // Clear requested owner
306     }
307 }
308 
309 int Manager::onPgoodChanged(sd_bus_message* msg,
310                             void* userData,
311                             sd_bus_error* retError)
312 {
313     using properties = std::map < std::string,
314           sdbusplus::message::variant<int> >;
315     auto m = sdbusplus::message::message(msg);
316     // message type: sa{sv}as
317     std::string ignore;
318     properties props;
319     m.read(ignore, props);
320     for (const auto& item : props)
321     {
322         if (item.first == PGOOD_STR)
323         {
324             static_cast<Manager*>(userData)
325                 ->onPgoodChanged(static_cast<bool>(item.second.get<int>()));
326         }
327     }
328     return 0;
329 }
330 
331 bool Manager::setCurrentTimeMode(const std::string& mode)
332 {
333     auto newMode = utils::strToMode(mode);
334     if (newMode != timeMode)
335     {
336         log<level::INFO>("Time mode is changed",
337                          entry("MODE=%s", mode.c_str()));
338         timeMode = newMode;
339         utils::writeData(modeFile, mode);
340         return true;
341     }
342     else
343     {
344         return false;
345     }
346 }
347 
348 bool Manager::setCurrentTimeOwner(const std::string& owner)
349 {
350     auto newOwner = utils::strToOwner(owner);
351     if (newOwner != timeOwner)
352     {
353         log<level::INFO>("Time owner is changed",
354                          entry("OWNER=%s", owner.c_str()));
355         timeOwner = newOwner;
356         utils::writeData(ownerFile, owner);
357         return true;
358     }
359     else
360     {
361         return false;
362     }
363 }
364 
365 void Manager::onTimeModeChanged(const std::string& mode)
366 {
367     for (const auto listener : listeners)
368     {
369         listener->onModeChanged(timeMode);
370     }
371     // When time_mode is updated, update the NTP setting
372     updateNtpSetting(mode);
373 }
374 
375 void Manager::onTimeOwnerChanged()
376 {
377     for (const auto& listener : listeners)
378     {
379         listener->onOwnerChanged(timeOwner);
380     }
381 }
382 
383 // TODO: This function is here only for use_dhcp_ntp.
384 // When use_dhcp_ntp is transferred to new settings daemon,
385 // this function can be removed.
386 std::string Manager::getSettings(const char* setting) const
387 {
388     std::string settingsService = utils::getService(bus,
389                                                     SETTINGS_PATH,
390                                                     SETTINGS_INTERFACE);
391 
392     return utils::getProperty<std::string>(bus,
393                                            settingsService.c_str(),
394                                            SETTINGS_PATH,
395                                            SETTINGS_INTERFACE,
396                                            setting);
397 }
398 
399 std::string Manager::getSetting(const char* path,
400                                 const char* interface,
401                                 const char* setting) const
402 {
403     std::string settingManager = utils::getService(bus, path, interface);
404     return utils::getProperty<std::string>(bus,
405                                            settingManager.c_str(),
406                                            path,
407                                            interface,
408                                            setting);
409 }
410 
411 }
412 }
413