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 32 Manager::Manager(sdbusplus::bus::bus& bus) : bus(bus), settings(bus) 33 { 34 using namespace sdbusplus::bus::match::rules; 35 hostStateChangeMatch = 36 std::make_unique<decltype(hostStateChangeMatch)::element_type>( 37 bus, propertiesChanged(settings.hostState, settings::hostStateIntf), 38 std::bind(std::mem_fn(&Manager::onHostStateChanged), this, 39 std::placeholders::_1)); 40 settingsMatches.emplace_back( 41 bus, propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf), 42 std::bind(std::mem_fn(&Manager::onSettingsChanged), this, 43 std::placeholders::_1)); 44 45 checkHostOn(); 46 47 // Restore settings from persistent storage 48 restoreSettings(); 49 50 // Check the settings daemon to process the new settings 51 auto mode = getSetting(settings.timeSyncMethod.c_str(), 52 settings::timeSyncIntf, PROPERTY_TIME_MODE); 53 54 onPropertyChanged(PROPERTY_TIME_MODE, mode); 55 } 56 57 void Manager::addListener(PropertyChangeListner* listener) 58 { 59 // Notify listener about the initial value 60 listener->onModeChanged(timeMode); 61 62 listeners.insert(listener); 63 } 64 65 void Manager::restoreSettings() 66 { 67 auto mode = utils::readData<std::string>(modeFile); 68 if (!mode.empty()) 69 { 70 timeMode = utils::strToMode(mode); 71 } 72 } 73 74 void Manager::checkHostOn() 75 { 76 using Host = sdbusplus::xyz::openbmc_project::State::server::Host; 77 auto hostService = utils::getService(bus, settings.hostState.c_str(), 78 settings::hostStateIntf); 79 auto stateStr = utils::getProperty<std::string>( 80 bus, hostService.c_str(), settings.hostState.c_str(), 81 settings::hostStateIntf, HOST_CURRENT_STATE); 82 auto state = Host::convertHostStateFromString(stateStr); 83 hostOn = (state == Host::HostState::Running); 84 } 85 86 void Manager::onPropertyChanged(const std::string& key, 87 const std::string& value) 88 { 89 if (hostOn) 90 { 91 // If host is on, set the values as requested time mode. 92 // And when host becomes off, notify the listeners. 93 setPropertyAsRequested(key, value); 94 } 95 else 96 { 97 // If host is off, notify listeners 98 if (key == PROPERTY_TIME_MODE) 99 { 100 setCurrentTimeMode(value); 101 onTimeModeChanged(value); 102 } 103 } 104 } 105 106 int Manager::onSettingsChanged(sdbusplus::message::message& msg) 107 { 108 using Interface = std::string; 109 using Property = std::string; 110 using Value = std::string; 111 using Properties = std::map<Property, std::variant<Value>>; 112 113 Interface interface; 114 Properties properties; 115 116 msg.read(interface, properties); 117 118 for (const auto& p : properties) 119 { 120 onPropertyChanged(p.first, std::get<std::string>(p.second)); 121 } 122 123 return 0; 124 } 125 126 void Manager::setPropertyAsRequested(const std::string& key, 127 const std::string& value) 128 { 129 if (key == PROPERTY_TIME_MODE) 130 { 131 setRequestedMode(value); 132 } 133 else 134 { 135 // The key shall be already the supported one 136 using InvalidArgumentError = 137 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 138 using namespace xyz::openbmc_project::Common; 139 elog<InvalidArgumentError>( 140 InvalidArgument::ARGUMENT_NAME(key.c_str()), 141 InvalidArgument::ARGUMENT_VALUE(value.c_str())); 142 } 143 } 144 145 void Manager::setRequestedMode(const std::string& mode) 146 { 147 requestedMode = mode; 148 } 149 150 void Manager::updateNtpSetting(const std::string& value) 151 { 152 bool isNtp = 153 (value == "xyz.openbmc_project.Time.Synchronization.Method.NTP"); 154 auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE, SYSTEMD_TIME_PATH, 155 SYSTEMD_TIME_INTERFACE, METHOD_SET_NTP); 156 method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable 157 // 'false' meaning no policy-kit 158 159 try 160 { 161 bus.call_noreply(method); 162 log<level::INFO>("Updated NTP setting", entry("ENABLED=%d", isNtp)); 163 } 164 catch (const sdbusplus::exception::SdBusError& ex) 165 { 166 log<level::ERR>("Failed to update NTP setting", 167 entry("ERR=%s", ex.what())); 168 } 169 } 170 171 void Manager::onHostStateChanged(sdbusplus::message::message& msg) 172 { 173 using Interface = std::string; 174 using Property = std::string; 175 using Value = std::string; 176 using Properties = std::map<Property, std::variant<Value>>; 177 using Host = sdbusplus::xyz::openbmc_project::State::server::Host; 178 179 Interface interface; 180 Properties properties; 181 182 msg.read(interface, properties); 183 184 for (const auto& p : properties) 185 { 186 if (p.first == HOST_CURRENT_STATE) 187 { 188 auto state = Host::convertHostStateFromString( 189 std::get<std::string>(p.second)); 190 onHostState(state == Host::HostState::Running); 191 break; 192 } 193 } 194 } 195 196 void Manager::onHostState(bool on) 197 { 198 hostOn = on; 199 if (hostOn) 200 { 201 log<level::INFO>("Changing time settings is *deferred* now"); 202 return; 203 } 204 log<level::INFO>("Changing time settings allowed now"); 205 if (!requestedMode.empty()) 206 { 207 if (setCurrentTimeMode(requestedMode)) 208 { 209 onTimeModeChanged(requestedMode); 210 } 211 setRequestedMode({}); // Clear requested mode 212 } 213 } 214 215 bool Manager::setCurrentTimeMode(const std::string& mode) 216 { 217 auto newMode = utils::strToMode(mode); 218 if (newMode != timeMode) 219 { 220 log<level::INFO>("Time mode is changed", 221 entry("MODE=%s", mode.c_str())); 222 timeMode = newMode; 223 utils::writeData(modeFile, mode); 224 return true; 225 } 226 else 227 { 228 return false; 229 } 230 } 231 232 void Manager::onTimeModeChanged(const std::string& mode) 233 { 234 for (const auto listener : listeners) 235 { 236 listener->onModeChanged(timeMode); 237 } 238 // When time_mode is updated, update the NTP setting 239 updateNtpSetting(mode); 240 } 241 242 std::string Manager::getSetting(const char* path, const char* interface, 243 const char* setting) const 244 { 245 std::string settingManager = utils::getService(bus, path, interface); 246 return utils::getProperty<std::string>(bus, settingManager.c_str(), path, 247 interface, setting); 248 } 249 250 } // namespace time 251 } // namespace phosphor 252