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 9 namespace rules = sdbusplus::bus::match::rules; 10 11 namespace // anonymous 12 { 13 constexpr auto SETTINGS_PATH = "/org/openbmc/settings/host0"; 14 constexpr auto SETTINGS_INTERFACE = "org.openbmc.settings.Host"; 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 constexpr auto POWER_PATH = "/org/openbmc/control/power0"; 30 constexpr auto POWER_INTERFACE = "org.openbmc.control.Power"; 31 constexpr auto PGOOD_STR = "pgood"; 32 33 constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1"; 34 constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1"; 35 constexpr auto SYSTEMD_TIME_INTERFACE = "org.freedesktop.timedate1"; 36 constexpr auto METHOD_SET_NTP = "SetNTP"; 37 38 constexpr auto OBMC_NETWORK_PATH = "/org/openbmc/NetworkManager/Interface"; 39 constexpr auto OBMC_NETWORK_INTERFACE = "org.openbmc.NetworkManager"; 40 constexpr auto METHOD_UPDATE_USE_NTP = "UpdateUseNtpField"; 41 } 42 43 namespace phosphor 44 { 45 namespace time 46 { 47 48 using namespace phosphor::logging; 49 50 const std::set<std::string> 51 Manager::managedProperties = {PROPERTY_TIME_MODE, PROPERTY_TIME_OWNER}; 52 53 Manager::Manager(sdbusplus::bus::bus& bus) 54 : bus(bus), 55 propertyChangeMatch(bus, MATCH_PROPERTY_CHANGE, onPropertyChanged, this), 56 pgoodChangeMatch(bus, MATCH_PGOOD_CHANGE, onPgoodChanged, this) 57 { 58 using namespace sdbusplus::bus::match::rules; 59 settingsMatches.emplace_back( 60 bus, 61 propertiesChanged(settings.timeOwner, settings::timeOwnerIntf), 62 std::bind(std::mem_fn(&Manager::onSettingsChanged), 63 this, std::placeholders::_1)); 64 settingsMatches.emplace_back( 65 bus, 66 propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf), 67 std::bind(std::mem_fn(&Manager::onSettingsChanged), 68 this, std::placeholders::_1)); 69 70 checkHostOn(); 71 72 // Restore settings from persistent storage 73 restoreSettings(); 74 75 // Check the settings daemon to process the new settings 76 auto mode = getSetting(settings.timeSyncMethod.c_str(), 77 settings::timeSyncIntf, 78 PROPERTY_TIME_MODE); 79 auto owner = getSetting(settings.timeOwner.c_str(), 80 settings::timeOwnerIntf, 81 PROPERTY_TIME_OWNER); 82 83 onPropertyChanged(PROPERTY_TIME_MODE, mode); 84 onPropertyChanged(PROPERTY_TIME_OWNER, owner); 85 86 checkDhcpNtp(); 87 } 88 89 void Manager::addListener(PropertyChangeListner* listener) 90 { 91 // Notify listener about the initial value 92 listener->onModeChanged(timeMode); 93 listener->onOwnerChanged(timeOwner); 94 95 listeners.insert(listener); 96 } 97 98 void Manager::restoreSettings() 99 { 100 auto mode = utils::readData<std::string>(modeFile); 101 if (!mode.empty()) 102 { 103 timeMode = utils::strToMode(mode); 104 } 105 auto owner = utils::readData<std::string>(ownerFile); 106 if (!owner.empty()) 107 { 108 timeOwner = utils::strToOwner(owner); 109 } 110 } 111 112 void Manager::checkHostOn() 113 { 114 std::string powerService = utils::getService(bus, 115 POWER_PATH, 116 POWER_INTERFACE); 117 118 int pgood = utils::getProperty<int>(bus, 119 powerService.c_str(), 120 POWER_PATH, 121 POWER_INTERFACE, 122 PGOOD_STR); 123 hostOn = static_cast<bool>(pgood); 124 } 125 126 void Manager::checkDhcpNtp() 127 { 128 std::string useDhcpNtp = getSettings(PROPERTY_DHCP_NTP); 129 updateDhcpNtpSetting(useDhcpNtp); 130 } 131 132 void Manager::onPropertyChanged(const std::string& key, 133 const std::string& value) 134 { 135 if (hostOn) 136 { 137 // If host is on, set the values as requested time mode/owner. 138 // And when host becomes off, notify the listners. 139 setPropertyAsRequested(key, value); 140 } 141 else 142 { 143 // If host is off, notify listners 144 if (key == PROPERTY_TIME_MODE) 145 { 146 setCurrentTimeMode(value); 147 onTimeModeChanged(value); 148 } 149 else if (key == PROPERTY_TIME_OWNER) 150 { 151 setCurrentTimeOwner(value); 152 onTimeOwnerChanged(); 153 } 154 } 155 } 156 157 int Manager::onPropertyChanged(sd_bus_message* msg, 158 void* userData, 159 sd_bus_error* retError) 160 { 161 using properties = std::map < std::string, 162 sdbusplus::message::variant<std::string> >; 163 auto m = sdbusplus::message::message(msg); 164 // message type: sa{sv}as 165 std::string ignore; 166 properties props; 167 m.read(ignore, props); 168 auto manager = static_cast<Manager*>(userData); 169 for (const auto& item : props) 170 { 171 if (managedProperties.find(item.first) != managedProperties.end()) 172 { 173 // For managed properties, notify listeners 174 manager->onPropertyChanged( 175 item.first, item.second.get<std::string>()); 176 } 177 else if (item.first == PROPERTY_DHCP_NTP) 178 { 179 // For other manager interested properties, handle specifically 180 manager->updateDhcpNtpSetting(item.second.get<std::string>()); 181 } 182 } 183 return 0; 184 } 185 186 int Manager::onSettingsChanged(sdbusplus::message::message& msg) 187 { 188 using Interface = std::string; 189 using Property = std::string; 190 using Value = std::string; 191 using Properties = std::map<Property, sdbusplus::message::variant<Value>>; 192 193 Interface interface; 194 Properties properties; 195 196 msg.read(interface, properties); 197 198 for(const auto& p : properties) 199 { 200 onPropertyChanged(p.first, p.second.get<std::string>()); 201 } 202 203 return 0; 204 } 205 206 void Manager::setPropertyAsRequested(const std::string& key, 207 const std::string& value) 208 { 209 if (key == PROPERTY_TIME_MODE) 210 { 211 setRequestedMode(value); 212 } 213 else if (key == PROPERTY_TIME_OWNER) 214 { 215 setRequestedOwner(value); 216 } 217 else 218 { 219 // The key shall be already the supported one 220 using InvalidArgumentError = 221 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 222 using namespace xyz::openbmc_project::Common; 223 elog<InvalidArgumentError>( 224 InvalidArgument::ARGUMENT_NAME(key.c_str()), 225 InvalidArgument::ARGUMENT_VALUE(value.c_str())); 226 } 227 } 228 229 void Manager::setRequestedMode(const std::string& mode) 230 { 231 requestedMode = mode; 232 } 233 234 void Manager::setRequestedOwner(const std::string& owner) 235 { 236 requestedOwner = owner; 237 } 238 239 void Manager::updateNtpSetting(const std::string& value) 240 { 241 bool isNtp = 242 (value == "xyz.openbmc_project.Time.Synchronization.Method.NTP"); 243 auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, 244 SYSTEMD_TIME_PATH, 245 SYSTEMD_TIME_INTERFACE, 246 METHOD_SET_NTP); 247 method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable 248 // 'false' meaning no policy-kit 249 250 if (bus.call(method)) 251 { 252 log<level::INFO>("Updated NTP setting", 253 entry("ENABLED:%d", isNtp)); 254 } 255 else 256 { 257 log<level::ERR>("Failed to update NTP setting"); 258 } 259 } 260 261 void Manager::updateDhcpNtpSetting(const std::string& useDhcpNtp) 262 { 263 std::string networkService = utils::getService(bus, 264 OBMC_NETWORK_PATH, 265 OBMC_NETWORK_INTERFACE); 266 267 auto method = bus.new_method_call(networkService.c_str(), 268 OBMC_NETWORK_PATH, 269 OBMC_NETWORK_INTERFACE, 270 METHOD_UPDATE_USE_NTP); 271 method.append(useDhcpNtp); 272 273 if (bus.call(method)) 274 { 275 log<level::INFO>("Updated use ntp field", 276 entry("USENTPFIELD:%s", useDhcpNtp.c_str())); 277 } 278 else 279 { 280 log<level::ERR>("Failed to update UseNtpField"); 281 } 282 } 283 284 void Manager::onPgoodChanged(bool pgood) 285 { 286 hostOn = pgood; 287 if (hostOn) 288 { 289 return; 290 } 291 if (!requestedMode.empty()) 292 { 293 if (setCurrentTimeMode(requestedMode)) 294 { 295 onTimeModeChanged(requestedMode); 296 } 297 setRequestedMode({}); // Clear requested mode 298 } 299 if (!requestedOwner.empty()) 300 { 301 if (setCurrentTimeOwner(requestedOwner)) 302 { 303 onTimeOwnerChanged(); 304 } 305 setRequestedOwner({}); // Clear requested owner 306 } 307 } 308 309 int Manager::onPgoodChanged(sd_bus_message* msg, 310 void* userData, 311 sd_bus_error* retError) 312 { 313 using properties = std::map < std::string, 314 sdbusplus::message::variant<int> >; 315 auto m = sdbusplus::message::message(msg); 316 // message type: sa{sv}as 317 std::string ignore; 318 properties props; 319 m.read(ignore, props); 320 for (const auto& item : props) 321 { 322 if (item.first == PGOOD_STR) 323 { 324 static_cast<Manager*>(userData) 325 ->onPgoodChanged(static_cast<bool>(item.second.get<int>())); 326 } 327 } 328 return 0; 329 } 330 331 bool Manager::setCurrentTimeMode(const std::string& mode) 332 { 333 auto newMode = utils::strToMode(mode); 334 if (newMode != timeMode) 335 { 336 log<level::INFO>("Time mode is changed", 337 entry("MODE=%s", mode.c_str())); 338 timeMode = newMode; 339 utils::writeData(modeFile, mode); 340 return true; 341 } 342 else 343 { 344 return false; 345 } 346 } 347 348 bool Manager::setCurrentTimeOwner(const std::string& owner) 349 { 350 auto newOwner = utils::strToOwner(owner); 351 if (newOwner != timeOwner) 352 { 353 log<level::INFO>("Time owner is changed", 354 entry("OWNER=%s", owner.c_str())); 355 timeOwner = newOwner; 356 utils::writeData(ownerFile, owner); 357 return true; 358 } 359 else 360 { 361 return false; 362 } 363 } 364 365 void Manager::onTimeModeChanged(const std::string& mode) 366 { 367 for (const auto listener : listeners) 368 { 369 listener->onModeChanged(timeMode); 370 } 371 // When time_mode is updated, update the NTP setting 372 updateNtpSetting(mode); 373 } 374 375 void Manager::onTimeOwnerChanged() 376 { 377 for (const auto& listener : listeners) 378 { 379 listener->onOwnerChanged(timeOwner); 380 } 381 } 382 383 // TODO: This function is here only for use_dhcp_ntp. 384 // When use_dhcp_ntp is transferred to new settings daemon, 385 // this function can be removed. 386 std::string Manager::getSettings(const char* setting) const 387 { 388 std::string settingsService = utils::getService(bus, 389 SETTINGS_PATH, 390 SETTINGS_INTERFACE); 391 392 return utils::getProperty<std::string>(bus, 393 settingsService.c_str(), 394 SETTINGS_PATH, 395 SETTINGS_INTERFACE, 396 setting); 397 } 398 399 std::string Manager::getSetting(const char* path, 400 const char* interface, 401 const char* setting) const 402 { 403 std::string settingManager = utils::getService(bus, path, interface); 404 return utils::getProperty<std::string>(bus, 405 settingManager.c_str(), 406 path, 407 interface, 408 setting); 409 } 410 411 } 412 } 413