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 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
14 constexpr auto METHOD_GET = "Get";
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 // TODO: consider put the get properties related functions into a common place
30 constexpr auto POWER_SERVICE = "org.openbmc.control.Power";
31 constexpr auto POWER_PATH = "/org/openbmc/control/power0";
32 constexpr auto POWER_INTERFACE = POWER_SERVICE;
33 constexpr auto PGOOD_STR = "pgood";
34 }
35 
36 namespace phosphor
37 {
38 namespace time
39 {
40 
41 using namespace phosphor::logging;
42 
43 const std::set<std::string>
44 Manager::managedProperties = {PROPERTY_TIME_MODE, PROPERTY_TIME_OWNER};
45 
46 const std::map<std::string, Owner> Manager::ownerMap =
47 {
48     { "BMC", Owner::BMC },
49     { "HOST", Owner::HOST },
50     { "SPLIT", Owner::SPLIT },
51     { "BOTH", Owner::BOTH },
52 };
53 
54 Manager::Manager(sdbusplus::bus::bus& bus)
55     : bus(bus),
56       propertyChangeMatch(bus, MATCH_PROPERTY_CHANGE, onPropertyChanged, this),
57       pgoodChangeMatch(bus, MATCH_PGOOD_CHANGE, onPgoodChanged, this)
58 {
59     checkHostOn();
60 
61     // Restore settings from persistent storage
62     restoreSettings();
63 
64     // Check the settings daemon to process the new settings
65     onPropertyChanged(PROPERTY_TIME_MODE, getSettings(PROPERTY_TIME_MODE));
66     onPropertyChanged(PROPERTY_TIME_OWNER, getSettings(PROPERTY_TIME_OWNER));
67 }
68 
69 void Manager::addListener(PropertyChangeListner* listener)
70 {
71     // Notify listener about the initial value
72     listener->onModeChanged(timeMode);
73     listener->onOwnerChanged(timeOwner);
74 
75     listeners.insert(listener);
76 }
77 
78 void Manager::restoreSettings()
79 {
80     auto mode = utils::readData<std::string>(modeFile);
81     if (!mode.empty())
82     {
83         timeMode = convertToMode(mode);
84     }
85     auto owner = utils::readData<std::string>(ownerFile);
86     if (!owner.empty())
87     {
88         timeOwner = convertToOwner(owner);
89     }
90 }
91 
92 void Manager::checkHostOn()
93 {
94     sdbusplus::message::variant<int> pgood = 0;
95     auto method = bus.new_method_call(POWER_SERVICE,
96                                       POWER_PATH,
97                                       PROPERTY_INTERFACE,
98                                       METHOD_GET);
99     method.append(PROPERTY_INTERFACE, PGOOD_STR);
100     auto reply = bus.call(method);
101     if (reply)
102     {
103         reply.read(pgood);
104     }
105 
106     hostOn = static_cast<bool>(pgood.get<int>());
107 }
108 
109 void Manager::onPropertyChanged(const std::string& key,
110                                 const std::string& value)
111 {
112     // TODO: Check dhcp_ntp
113 
114     if (hostOn)
115     {
116         // If host is on, set the values as requested time mode/owner.
117         // And when host becomes off, notify the listners.
118         setPropertyAsRequested(key, value);
119     }
120     else
121     {
122         // If host is off, notify listners
123         if (key == PROPERTY_TIME_MODE)
124         {
125             setCurrentTimeMode(value);
126             for (const auto listener : listeners)
127             {
128                 listener->onModeChanged(timeMode);
129             }
130         }
131         else if (key == PROPERTY_TIME_OWNER)
132         {
133             setCurrentTimeOwner(value);
134             for (const auto listener : listeners)
135             {
136                 listener->onOwnerChanged(timeOwner);
137             }
138         }
139     }
140 }
141 
142 int Manager::onPropertyChanged(sd_bus_message* msg,
143                                void* userData,
144                                sd_bus_error* retError)
145 {
146     using properties = std::map < std::string,
147           sdbusplus::message::variant<std::string> >;
148     auto m = sdbusplus::message::message(msg);
149     // message type: sa{sv}as
150     std::string ignore;
151     properties props;
152     m.read(ignore, props);
153     for (const auto& item : props)
154     {
155         if (managedProperties.find(item.first) != managedProperties.end())
156         {
157             static_cast<Manager*>(userData)->onPropertyChanged(
158                 item.first, item.second.get<std::string>());
159         }
160     }
161     return 0;
162 }
163 
164 void Manager::setPropertyAsRequested(const std::string& key,
165                                      const std::string& value)
166 {
167     if (key == PROPERTY_TIME_MODE)
168     {
169         setRequestedMode(value);
170     }
171     else if (key == PROPERTY_TIME_OWNER)
172     {
173         setRequestedOwner(value);
174     }
175     else
176     {
177         // The key shall be already the supported one
178         // TODO: use elog API
179         assert(false);
180     }
181 }
182 
183 void Manager::setRequestedMode(const std::string& mode)
184 {
185     requestedMode = mode;
186 }
187 
188 void Manager::setRequestedOwner(const std::string& owner)
189 {
190     requestedOwner = owner;
191 }
192 
193 void Manager::onPgoodChanged(bool pgood)
194 {
195     hostOn = pgood;
196     if (hostOn)
197     {
198         return;
199     }
200     if (!requestedMode.empty())
201     {
202         setCurrentTimeMode(requestedMode);
203         for (const auto& listener : listeners)
204         {
205             listener->onModeChanged(timeMode);
206         }
207         setRequestedMode({}); // Clear requested mode
208     }
209     if (!requestedOwner.empty())
210     {
211         setCurrentTimeOwner(requestedOwner);
212         for (const auto& listener : listeners)
213         {
214             listener->onOwnerChanged(timeOwner);
215         }
216         setRequestedOwner({}); // Clear requested owner
217     }
218 }
219 
220 int Manager::onPgoodChanged(sd_bus_message* msg,
221                             void* userData,
222                             sd_bus_error* retError)
223 {
224     using properties = std::map < std::string,
225           sdbusplus::message::variant<int> >;
226     auto m = sdbusplus::message::message(msg);
227     // message type: sa{sv}as
228     std::string ignore;
229     properties props;
230     m.read(ignore, props);
231     for (const auto& item : props)
232     {
233         if (item.first == PGOOD_STR)
234         {
235             static_cast<Manager*>(userData)
236                 ->onPgoodChanged(static_cast<bool>(item.second.get<int>()));
237         }
238     }
239     return 0;
240 }
241 
242 void Manager::setCurrentTimeMode(const std::string& mode)
243 {
244     log<level::INFO>("Time mode is changed",
245                      entry("MODE=%s", mode.c_str()));
246     timeMode = convertToMode(mode);
247     utils::writeData(modeFile, mode);
248 }
249 
250 void Manager::setCurrentTimeOwner(const std::string& owner)
251 {
252     log<level::INFO>("Time owner is changed",
253                      entry("OWNER=%s", owner.c_str()));
254     timeOwner = convertToOwner(owner);
255     utils::writeData(ownerFile, owner);
256 }
257 
258 std::string Manager::getSettings(const char* value) const
259 {
260     sdbusplus::message::variant<std::string> mode;
261     auto method = bus.new_method_call(SETTINGS_SERVICE,
262                                       SETTINGS_PATH,
263                                       PROPERTY_INTERFACE,
264                                       METHOD_GET);
265     method.append(SETTINGS_INTERFACE, value);
266     auto reply = bus.call(method);
267     if (reply)
268     {
269         reply.read(mode);
270     }
271     else
272     {
273         log<level::ERR>("Failed to get settings");
274     }
275 
276     return mode.get<std::string>();
277 }
278 
279 Mode Manager::convertToMode(const std::string& mode)
280 {
281     if (mode == "NTP")
282     {
283         return Mode::NTP;
284     }
285     else if (mode == "MANUAL")
286     {
287         return Mode::MANUAL;
288     }
289     else
290     {
291         log<level::ERR>("Unrecognized mode",
292                         entry("%s", mode.c_str()));
293         return Mode::NTP;
294     }
295 }
296 
297 Owner Manager::convertToOwner(const std::string& owner)
298 {
299     auto it = ownerMap.find(owner);
300     if (it == ownerMap.end())
301     {
302         log<level::ERR>("Unrecognized owner",
303                         entry("%s", owner.c_str()));
304         return Owner::BMC;
305     }
306     return it->second;
307 }
308 
309 }
310 }
311