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     if (bus.call(method))
198     {
199         log<level::INFO>("Updated NTP setting",
200                          entry("ENABLED:%d", isNtp));
201     }
202     else
203     {
204         log<level::ERR>("Failed to update NTP setting");
205     }
206 }
207 
208 void Manager::onHostStateChanged(sdbusplus::message::message& msg)
209 {
210     using Interface = std::string;
211     using Property = std::string;
212     using Value = std::string;
213     using Properties = std::map<Property, sdbusplus::message::variant<Value>>;
214     using Host = sdbusplus::xyz::openbmc_project::State::server::Host;
215 
216     Interface interface;
217     Properties properties;
218 
219     msg.read(interface, properties);
220 
221     for(const auto& p : properties)
222     {
223         if (p.first == HOST_CURRENT_STATE)
224         {
225             auto state = Host::convertHostStateFromString(p.second.get<std::string>());
226             onHostState(state == Host::HostState::Running);
227             break;
228         }
229     }
230 }
231 
232 void Manager::onHostState(bool on)
233 {
234     hostOn = on;
235     if (hostOn)
236     {
237         log<level::INFO>("Changing time settings is *deferred* now");
238         return;
239     }
240     log<level::INFO>("Changing time settings allowed now");
241     if (!requestedMode.empty())
242     {
243         if (setCurrentTimeMode(requestedMode))
244         {
245             onTimeModeChanged(requestedMode);
246         }
247         setRequestedMode({}); // Clear requested mode
248     }
249     if (!requestedOwner.empty())
250     {
251         if (setCurrentTimeOwner(requestedOwner))
252         {
253             onTimeOwnerChanged();
254         }
255         setRequestedOwner({}); // Clear requested owner
256     }
257 }
258 
259 bool Manager::setCurrentTimeMode(const std::string& mode)
260 {
261     auto newMode = utils::strToMode(mode);
262     if (newMode != timeMode)
263     {
264         log<level::INFO>("Time mode is changed",
265                          entry("MODE=%s", mode.c_str()));
266         timeMode = newMode;
267         utils::writeData(modeFile, mode);
268         return true;
269     }
270     else
271     {
272         return false;
273     }
274 }
275 
276 bool Manager::setCurrentTimeOwner(const std::string& owner)
277 {
278     auto newOwner = utils::strToOwner(owner);
279     if (newOwner != timeOwner)
280     {
281         log<level::INFO>("Time owner is changed",
282                          entry("OWNER=%s", owner.c_str()));
283         timeOwner = newOwner;
284         utils::writeData(ownerFile, owner);
285         return true;
286     }
287     else
288     {
289         return false;
290     }
291 }
292 
293 void Manager::onTimeModeChanged(const std::string& mode)
294 {
295     for (const auto listener : listeners)
296     {
297         listener->onModeChanged(timeMode);
298     }
299     // When time_mode is updated, update the NTP setting
300     updateNtpSetting(mode);
301 }
302 
303 void Manager::onTimeOwnerChanged()
304 {
305     for (const auto& listener : listeners)
306     {
307         listener->onOwnerChanged(timeOwner);
308     }
309 }
310 
311 std::string Manager::getSetting(const char* path,
312                                 const char* interface,
313                                 const char* setting) const
314 {
315     std::string settingManager = utils::getService(bus, path, interface);
316     return utils::getProperty<std::string>(bus,
317                                            settingManager.c_str(),
318                                            path,
319                                            interface,
320                                            setting);
321 }
322 
323 }
324 }
325