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     checkHostOn();
59 
60     // Restore settings from persistent storage
61     restoreSettings();
62 
63     // Check the settings daemon to process the new settings
64     onPropertyChanged(PROPERTY_TIME_MODE, getSettings(PROPERTY_TIME_MODE));
65     onPropertyChanged(PROPERTY_TIME_OWNER, getSettings(PROPERTY_TIME_OWNER));
66 
67     checkDhcpNtp();
68 }
69 
70 void Manager::addListener(PropertyChangeListner* listener)
71 {
72     // Notify listener about the initial value
73     listener->onModeChanged(timeMode);
74     listener->onOwnerChanged(timeOwner);
75 
76     listeners.insert(listener);
77 }
78 
79 void Manager::restoreSettings()
80 {
81     auto mode = utils::readData<std::string>(modeFile);
82     if (!mode.empty())
83     {
84         timeMode = utils::strToMode(mode);
85     }
86     auto owner = utils::readData<std::string>(ownerFile);
87     if (!owner.empty())
88     {
89         timeOwner = utils::strToOwner(owner);
90     }
91 }
92 
93 void Manager::checkHostOn()
94 {
95     std::string powerService = utils::getService(bus,
96                                                  POWER_PATH,
97                                                  POWER_INTERFACE);
98 
99     int pgood = utils::getProperty<int>(bus,
100                                         powerService.c_str(),
101                                         POWER_PATH,
102                                         POWER_INTERFACE,
103                                         PGOOD_STR);
104     hostOn = static_cast<bool>(pgood);
105 }
106 
107 void Manager::checkDhcpNtp()
108 {
109     std::string useDhcpNtp = getSettings(PROPERTY_DHCP_NTP);
110     updateDhcpNtpSetting(useDhcpNtp);
111 }
112 
113 void Manager::onPropertyChanged(const std::string& key,
114                                 const std::string& value)
115 {
116     if (hostOn)
117     {
118         // If host is on, set the values as requested time mode/owner.
119         // And when host becomes off, notify the listners.
120         setPropertyAsRequested(key, value);
121     }
122     else
123     {
124         // If host is off, notify listners
125         if (key == PROPERTY_TIME_MODE)
126         {
127             setCurrentTimeMode(value);
128             onTimeModeChanged(value);
129         }
130         else if (key == PROPERTY_TIME_OWNER)
131         {
132             setCurrentTimeOwner(value);
133             onTimeOwnerChanged();
134         }
135     }
136 }
137 
138 int Manager::onPropertyChanged(sd_bus_message* msg,
139                                void* userData,
140                                sd_bus_error* retError)
141 {
142     using properties = std::map < std::string,
143           sdbusplus::message::variant<std::string> >;
144     auto m = sdbusplus::message::message(msg);
145     // message type: sa{sv}as
146     std::string ignore;
147     properties props;
148     m.read(ignore, props);
149     auto manager = static_cast<Manager*>(userData);
150     for (const auto& item : props)
151     {
152         if (managedProperties.find(item.first) != managedProperties.end())
153         {
154             // For managed properties, notify listeners
155             manager->onPropertyChanged(
156                 item.first, item.second.get<std::string>());
157         }
158         else if (item.first == PROPERTY_DHCP_NTP)
159         {
160             // For other manager interested properties, handle specifically
161             manager->updateDhcpNtpSetting(item.second.get<std::string>());
162         }
163     }
164     return 0;
165 }
166 
167 void Manager::setPropertyAsRequested(const std::string& key,
168                                      const std::string& value)
169 {
170     if (key == PROPERTY_TIME_MODE)
171     {
172         setRequestedMode(value);
173     }
174     else if (key == PROPERTY_TIME_OWNER)
175     {
176         setRequestedOwner(value);
177     }
178     else
179     {
180         // The key shall be already the supported one
181         using InvalidArgumentError =
182             sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
183         using namespace xyz::openbmc_project::Common;
184         elog<InvalidArgumentError>(
185             InvalidArgument::ARGUMENT_NAME(key.c_str()),
186             InvalidArgument::ARGUMENT_VALUE(value.c_str()));
187     }
188 }
189 
190 void Manager::setRequestedMode(const std::string& mode)
191 {
192     requestedMode = mode;
193 }
194 
195 void Manager::setRequestedOwner(const std::string& owner)
196 {
197     requestedOwner = owner;
198 }
199 
200 void Manager::updateNtpSetting(const std::string& value)
201 {
202     bool isNtp = (value == "NTP");
203     auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE,
204                                       SYSTEMD_TIME_PATH,
205                                       SYSTEMD_TIME_INTERFACE,
206                                       METHOD_SET_NTP);
207     method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable
208                                  // 'false' meaning no policy-kit
209 
210     if (bus.call(method))
211     {
212         log<level::INFO>("Updated NTP setting",
213                          entry("ENABLED:%d", isNtp));
214     }
215     else
216     {
217         log<level::ERR>("Failed to update NTP setting");
218     }
219 }
220 
221 void Manager::updateDhcpNtpSetting(const std::string& useDhcpNtp)
222 {
223     std::string networkService = utils::getService(bus,
224                                                    OBMC_NETWORK_PATH,
225                                                    OBMC_NETWORK_INTERFACE);
226 
227     auto method = bus.new_method_call(networkService.c_str(),
228                                       OBMC_NETWORK_PATH,
229                                       OBMC_NETWORK_INTERFACE,
230                                       METHOD_UPDATE_USE_NTP);
231     method.append(useDhcpNtp);
232 
233     if (bus.call(method))
234     {
235         log<level::INFO>("Updated use ntp field",
236                          entry("USENTPFIELD:%s", useDhcpNtp.c_str()));
237     }
238     else
239     {
240         log<level::ERR>("Failed to update UseNtpField");
241     }
242 }
243 
244 void Manager::onPgoodChanged(bool pgood)
245 {
246     hostOn = pgood;
247     if (hostOn)
248     {
249         return;
250     }
251     if (!requestedMode.empty())
252     {
253         if (setCurrentTimeMode(requestedMode))
254         {
255             onTimeModeChanged(requestedMode);
256         }
257         setRequestedMode({}); // Clear requested mode
258     }
259     if (!requestedOwner.empty())
260     {
261         if (setCurrentTimeOwner(requestedOwner))
262         {
263             onTimeOwnerChanged();
264         }
265         setRequestedOwner({}); // Clear requested owner
266     }
267 }
268 
269 int Manager::onPgoodChanged(sd_bus_message* msg,
270                             void* userData,
271                             sd_bus_error* retError)
272 {
273     using properties = std::map < std::string,
274           sdbusplus::message::variant<int> >;
275     auto m = sdbusplus::message::message(msg);
276     // message type: sa{sv}as
277     std::string ignore;
278     properties props;
279     m.read(ignore, props);
280     for (const auto& item : props)
281     {
282         if (item.first == PGOOD_STR)
283         {
284             static_cast<Manager*>(userData)
285                 ->onPgoodChanged(static_cast<bool>(item.second.get<int>()));
286         }
287     }
288     return 0;
289 }
290 
291 bool Manager::setCurrentTimeMode(const std::string& mode)
292 {
293     auto newMode = utils::strToMode(mode);
294     if (newMode != timeMode)
295     {
296         log<level::INFO>("Time mode is changed",
297                          entry("MODE=%s", mode.c_str()));
298         timeMode = newMode;
299         utils::writeData(modeFile, mode);
300         return true;
301     }
302     else
303     {
304         return false;
305     }
306 }
307 
308 bool Manager::setCurrentTimeOwner(const std::string& owner)
309 {
310     auto newOwner = utils::strToOwner(owner);
311     if (newOwner != timeOwner)
312     {
313         log<level::INFO>("Time owner is changed",
314                          entry("OWNER=%s", owner.c_str()));
315         timeOwner = newOwner;
316         utils::writeData(ownerFile, owner);
317         return true;
318     }
319     else
320     {
321         return false;
322     }
323 }
324 
325 void Manager::onTimeModeChanged(const std::string& mode)
326 {
327     for (const auto listener : listeners)
328     {
329         listener->onModeChanged(timeMode);
330     }
331     // When time_mode is updated, update the NTP setting
332     updateNtpSetting(mode);
333 }
334 
335 void Manager::onTimeOwnerChanged()
336 {
337     for (const auto& listener : listeners)
338     {
339         listener->onOwnerChanged(timeOwner);
340     }
341 }
342 
343 std::string Manager::getSettings(const char* setting) const
344 {
345     std::string settingsService = utils::getService(bus,
346                                                     SETTINGS_PATH,
347                                                     SETTINGS_INTERFACE);
348 
349     return utils::getProperty<std::string>(bus,
350                                            settingsService.c_str(),
351                                            SETTINGS_PATH,
352                                            SETTINGS_INTERFACE,
353                                            setting);
354 }
355 
356 }
357 }
358