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