1 #include "manager.hpp"
2 
3 #include "utils.hpp"
4 
5 #include <phosphor-logging/elog-errors.hpp>
6 #include <phosphor-logging/elog.hpp>
7 #include <phosphor-logging/log.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9 #include <xyz/openbmc_project/State/Host/server.hpp>
10 
11 namespace rules = sdbusplus::bus::match::rules;
12 
13 namespace // anonymous
14 {
15 constexpr auto HOST_CURRENT_STATE = "CurrentHostState";
16 
17 constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1";
18 constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1";
19 constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1";
20 constexpr auto METHOD_SET_NTP = "SetNTP";
21 } // namespace
22 
23 namespace phosphor
24 {
25 namespace time
26 {
27 
28 using namespace phosphor::logging;
29 
30 const std::set<std::string> Manager::managedProperties = {PROPERTY_TIME_MODE,
31                                                           PROPERTY_TIME_OWNER};
32 
33 Manager::Manager(sdbusplus::bus::bus& bus) : bus(bus)
34 {
35     using namespace sdbusplus::bus::match::rules;
36     hostStateChangeMatch =
37         std::make_unique<decltype(hostStateChangeMatch)::element_type>(
38             bus, propertiesChanged(settings.hostState, settings::hostStateIntf),
39             std::bind(std::mem_fn(&Manager::onHostStateChanged), this,
40                       std::placeholders::_1));
41     settingsMatches.emplace_back(
42         bus, propertiesChanged(settings.timeOwner, settings::timeOwnerIntf),
43         std::bind(std::mem_fn(&Manager::onSettingsChanged), this,
44                   std::placeholders::_1));
45     settingsMatches.emplace_back(
46         bus, propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf),
47         std::bind(std::mem_fn(&Manager::onSettingsChanged), this,
48                   std::placeholders::_1));
49 
50     checkHostOn();
51 
52     // Restore settings from persistent storage
53     restoreSettings();
54 
55     // Check the settings daemon to process the new settings
56     auto mode = getSetting(settings.timeSyncMethod.c_str(),
57                            settings::timeSyncIntf, PROPERTY_TIME_MODE);
58     auto owner = getSetting(settings.timeOwner.c_str(), settings::timeOwnerIntf,
59                             PROPERTY_TIME_OWNER);
60 
61     onPropertyChanged(PROPERTY_TIME_MODE, mode);
62     onPropertyChanged(PROPERTY_TIME_OWNER, owner);
63 }
64 
65 void Manager::addListener(PropertyChangeListner* listener)
66 {
67     // Notify listener about the initial value
68     listener->onModeChanged(timeMode);
69     listener->onOwnerChanged(timeOwner);
70 
71     listeners.insert(listener);
72 }
73 
74 void Manager::restoreSettings()
75 {
76     auto mode = utils::readData<std::string>(modeFile);
77     if (!mode.empty())
78     {
79         timeMode = utils::strToMode(mode);
80     }
81     auto owner = utils::readData<std::string>(ownerFile);
82     if (!owner.empty())
83     {
84         timeOwner = utils::strToOwner(owner);
85     }
86 }
87 
88 void Manager::checkHostOn()
89 {
90     using Host = sdbusplus::xyz::openbmc_project::State::server::Host;
91     auto hostService = utils::getService(bus, settings.hostState.c_str(),
92                                          settings::hostStateIntf);
93     auto stateStr = utils::getProperty<std::string>(
94         bus, hostService.c_str(), settings.hostState.c_str(),
95         settings::hostStateIntf, HOST_CURRENT_STATE);
96     auto state = Host::convertHostStateFromString(stateStr);
97     hostOn = (state == Host::HostState::Running);
98 }
99 
100 void Manager::onPropertyChanged(const std::string& key,
101                                 const std::string& value)
102 {
103     if (hostOn)
104     {
105         // If host is on, set the values as requested time mode/owner.
106         // And when host becomes off, notify the listeners.
107         setPropertyAsRequested(key, value);
108     }
109     else
110     {
111         // If host is off, notify listeners
112         if (key == PROPERTY_TIME_MODE)
113         {
114             setCurrentTimeMode(value);
115             onTimeModeChanged(value);
116         }
117         else if (key == PROPERTY_TIME_OWNER)
118         {
119             setCurrentTimeOwner(value);
120             onTimeOwnerChanged();
121         }
122     }
123 }
124 
125 int Manager::onSettingsChanged(sdbusplus::message::message& msg)
126 {
127     using Interface = std::string;
128     using Property = std::string;
129     using Value = std::string;
130     using Properties = std::map<Property, sdbusplus::message::variant<Value>>;
131 
132     Interface interface;
133     Properties properties;
134 
135     msg.read(interface, properties);
136 
137     for (const auto& p : properties)
138     {
139         onPropertyChanged(p.first, p.second.get<std::string>());
140     }
141 
142     return 0;
143 }
144 
145 void Manager::setPropertyAsRequested(const std::string& key,
146                                      const std::string& value)
147 {
148     if (key == PROPERTY_TIME_MODE)
149     {
150         setRequestedMode(value);
151     }
152     else if (key == PROPERTY_TIME_OWNER)
153     {
154         setRequestedOwner(value);
155     }
156     else
157     {
158         // The key shall be already the supported one
159         using InvalidArgumentError =
160             sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
161         using namespace xyz::openbmc_project::Common;
162         elog<InvalidArgumentError>(
163             InvalidArgument::ARGUMENT_NAME(key.c_str()),
164             InvalidArgument::ARGUMENT_VALUE(value.c_str()));
165     }
166 }
167 
168 void Manager::setRequestedMode(const std::string& mode)
169 {
170     requestedMode = mode;
171 }
172 
173 void Manager::setRequestedOwner(const std::string& owner)
174 {
175     requestedOwner = owner;
176 }
177 
178 void Manager::updateNtpSetting(const std::string& value)
179 {
180     bool isNtp =
181         (value == "xyz.openbmc_project.Time.Synchronization.Method.NTP");
182     auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH,
183                                       SYSTEMD_TIME_INTERFACE, METHOD_SET_NTP);
184     method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable
185                                  // 'false' meaning no policy-kit
186 
187     try
188     {
189         bus.call_noreply(method);
190         log<level::INFO>("Updated NTP setting", entry("ENABLED=%d", isNtp));
191     }
192     catch (const sdbusplus::exception::SdBusError& ex)
193     {
194         log<level::ERR>("Failed to update NTP setting",
195                         entry("ERR=%s", ex.what()));
196     }
197 }
198 
199 void Manager::onHostStateChanged(sdbusplus::message::message& msg)
200 {
201     using Interface = std::string;
202     using Property = std::string;
203     using Value = std::string;
204     using Properties = std::map<Property, sdbusplus::message::variant<Value>>;
205     using Host = sdbusplus::xyz::openbmc_project::State::server::Host;
206 
207     Interface interface;
208     Properties properties;
209 
210     msg.read(interface, properties);
211 
212     for (const auto& p : properties)
213     {
214         if (p.first == HOST_CURRENT_STATE)
215         {
216             auto state =
217                 Host::convertHostStateFromString(p.second.get<std::string>());
218             onHostState(state == Host::HostState::Running);
219             break;
220         }
221     }
222 }
223 
224 void Manager::onHostState(bool on)
225 {
226     hostOn = on;
227     if (hostOn)
228     {
229         log<level::INFO>("Changing time settings is *deferred* now");
230         return;
231     }
232     log<level::INFO>("Changing time settings allowed now");
233     if (!requestedMode.empty())
234     {
235         if (setCurrentTimeMode(requestedMode))
236         {
237             onTimeModeChanged(requestedMode);
238         }
239         setRequestedMode({}); // Clear requested mode
240     }
241     if (!requestedOwner.empty())
242     {
243         if (setCurrentTimeOwner(requestedOwner))
244         {
245             onTimeOwnerChanged();
246         }
247         setRequestedOwner({}); // Clear requested owner
248     }
249 }
250 
251 bool Manager::setCurrentTimeMode(const std::string& mode)
252 {
253     auto newMode = utils::strToMode(mode);
254     if (newMode != timeMode)
255     {
256         log<level::INFO>("Time mode is changed",
257                          entry("MODE=%s", mode.c_str()));
258         timeMode = newMode;
259         utils::writeData(modeFile, mode);
260         return true;
261     }
262     else
263     {
264         return false;
265     }
266 }
267 
268 bool Manager::setCurrentTimeOwner(const std::string& owner)
269 {
270     auto newOwner = utils::strToOwner(owner);
271     if (newOwner != timeOwner)
272     {
273         log<level::INFO>("Time owner is changed",
274                          entry("OWNER=%s", owner.c_str()));
275         timeOwner = newOwner;
276         utils::writeData(ownerFile, owner);
277         return true;
278     }
279     else
280     {
281         return false;
282     }
283 }
284 
285 void Manager::onTimeModeChanged(const std::string& mode)
286 {
287     for (const auto listener : listeners)
288     {
289         listener->onModeChanged(timeMode);
290     }
291     // When time_mode is updated, update the NTP setting
292     updateNtpSetting(mode);
293 }
294 
295 void Manager::onTimeOwnerChanged()
296 {
297     for (const auto& listener : listeners)
298     {
299         listener->onOwnerChanged(timeOwner);
300     }
301 }
302 
303 std::string Manager::getSetting(const char* path, const char* interface,
304                                 const char* setting) const
305 {
306     std::string settingManager = utils::getService(bus, path, interface);
307     return utils::getProperty<std::string>(bus, settingManager.c_str(), path,
308                                            interface, setting);
309 }
310 
311 } // namespace time
312 } // namespace phosphor
313