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