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