ConfigurationStatus.cpp (16582B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "ConfigurationStatus.h" 5 #include "ConfigurationChangeData.h" 6 #include "ConfigurationProcessor.h" 7 #include "ConfigurationSet.h" 8 #include "ConfigurationUnitResultInformation.h" 9 #include <AppInstallerStrings.h> 10 #include <AppInstallerLanguageUtilities.h> 11 12 13 namespace winrt::Microsoft::Management::Configuration::implementation 14 { 15 namespace details 16 { 17 // Implements the consuming side of the status signaling. 18 struct ChangeListener 19 { 20 struct SetStatusItem 21 { 22 ConfigurationDatabase::StatusItem Status; 23 com_ptr<implementation::ConfigurationSet> Set; 24 }; 25 26 ChangeListener(ConfigurationStatus& status) : m_status(status) 27 { 28 ConfigurationDatabase::StatusBaseline baseline = m_status.Database().GetStatusBaseline(); 29 m_changeIdentifier = baseline.ChangeIdentifier; 30 31 for (const auto& item : baseline.SetStatus) 32 { 33 m_lastSetStatus.emplace(item.SetInstanceIdentifier, SetStatusItem{ item }); 34 } 35 36 std::wstring objectName = L"WinGetConfigListener_" + AppInstaller::Utility::CreateNewGuidNameWString(); 37 m_listenerEventName = AppInstaller::Utility::ConvertToUTF8(objectName); 38 m_listenerEvent.create(wil::EventOptions::None, objectName.c_str()); 39 40 m_status.Database().AddListener(m_listenerEventName); 41 42 m_threadPoolWait.reset(CreateThreadpoolWait(StaticWaitCallback, this, nullptr)); 43 THROW_LAST_ERROR_IF(!m_threadPoolWait); 44 45 SetThreadpoolWait(m_threadPoolWait.get(), m_listenerEvent.get(), NULL); 46 } 47 48 ~ChangeListener() 49 { 50 try 51 { 52 m_status.Database().RemoveListener(m_listenerEventName); 53 } 54 CATCH_LOG(); 55 } 56 57 private: 58 static void NTAPI StaticWaitCallback(PTP_CALLBACK_INSTANCE, void* context, TP_WAIT*, TP_WAIT_RESULT) 59 { 60 reinterpret_cast<ChangeListener*>(context)->WaitCallback(); 61 } 62 63 void WaitCallback() try 64 { 65 std::vector<ConfigurationDatabase::StatusItem> changes = m_status.Database().GetStatusSince(m_changeIdentifier); 66 67 // Convert status items to relevant change information 68 for (const auto& change : changes) 69 { 70 if (change.UnitInstanceIdentifier) 71 { 72 if (m_status.HasSetChangeRegistration(change.SetInstanceIdentifier)) 73 { 74 // A unit status change 75 ConfigurationUnitState state = AppInstaller::ToEnum<ConfigurationUnitState>(change.State); 76 77 decltype(make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) resultInformation; 78 79 if (change.ResultCode) 80 { 81 resultInformation = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); 82 resultInformation->ResultCode(change.ResultCode.value()); 83 resultInformation->Description(hstring{ AppInstaller::Utility::ConvertToUTF16(change.ResultDescription) }); 84 resultInformation->Details(hstring{ AppInstaller::Utility::ConvertToUTF16(change.ResultDetails) }); 85 resultInformation->ResultSource(change.ResultSource); 86 } 87 88 auto changeData = make_self<implementation::ConfigurationSetChangeData>(); 89 changeData->Initialize(state, *resultInformation, nullptr); 90 91 m_status.SetChangeDetected(change.SetInstanceIdentifier, changeData, change.UnitInstanceIdentifier); 92 } 93 } 94 else 95 { 96 // A set status change 97 ConfigurationSetState state = AppInstaller::ToEnum<ConfigurationSetState>(change.State); 98 ConfigurationChangeEventType changeType = ConfigurationChangeEventType::Unknown; 99 100 SetStatusItem* setStatusItem = nullptr; 101 auto itr = m_lastSetStatus.find(change.SetInstanceIdentifier); 102 if (itr != m_lastSetStatus.end()) 103 { 104 setStatusItem = &itr->second; 105 } 106 107 if (!setStatusItem) 108 { 109 changeType = ConfigurationChangeEventType::SetAdded; 110 111 std::tie(itr, std::ignore) = m_lastSetStatus.emplace(change.SetInstanceIdentifier, SetStatusItem{ change }); 112 setStatusItem = &itr->second; 113 } 114 else 115 { 116 changeType = (change.InQueue ? ConfigurationChangeEventType::SetStateChanged : ConfigurationChangeEventType::SetRemoved); 117 } 118 119 if (m_status.HasChangeRegistrations()) 120 { 121 if (!setStatusItem->Set) 122 { 123 setStatusItem->Set = m_status.Database().GetSet(change.SetInstanceIdentifier); 124 } 125 126 auto changeData = make_self<wil::details::module_count_wrapper<implementation::ConfigurationChangeData>>(); 127 changeData->Initialize(changeType, change.SetInstanceIdentifier, state); 128 129 m_status.ChangeDetected(*setStatusItem->Set, *changeData); 130 } 131 132 auto setChangeData = make_self<implementation::ConfigurationSetChangeData>(); 133 setChangeData->Initialize(state); 134 135 m_status.SetChangeDetected(change.SetInstanceIdentifier, setChangeData, std::nullopt); 136 } 137 138 m_changeIdentifier = change.ChangeIdentifier; 139 } 140 141 SetThreadpoolWait(m_threadPoolWait.get(), m_listenerEvent.get(), NULL); 142 } 143 CATCH_LOG_MSG("ChangeListener::WaitCallback exception"); 144 145 ConfigurationStatus& m_status; 146 int64_t m_changeIdentifier; 147 std::map<winrt::guid, SetStatusItem> m_lastSetStatus; 148 wil::unique_event m_listenerEvent; 149 std::string m_listenerEventName; 150 151 // Keep last to destroy first 152 wil::unique_threadpool_wait m_threadPoolWait; 153 }; 154 } 155 156 ConfigurationStatus::ConfigurationStatus(private_construction) {} 157 158 ConfigurationStatus::~ConfigurationStatus() = default; 159 160 std::shared_ptr<ConfigurationStatus> ConfigurationStatus::Instance() 161 { 162 static std::shared_ptr<ConfigurationStatus> s_instance; 163 164 std::shared_ptr<ConfigurationStatus> result = std::atomic_load(&s_instance); 165 if (!result) 166 { 167 result = std::make_shared<ConfigurationStatus>(private_construction{}); 168 std::shared_ptr<ConfigurationStatus> empty; 169 170 if (!std::atomic_compare_exchange_strong(&s_instance, &empty, result)) 171 { 172 result = empty; 173 } 174 } 175 176 return result; 177 } 178 179 ConfigurationSetState ConfigurationStatus::GetSetState(const winrt::guid& instanceIdentifier) 180 { 181 m_database.EnsureOpened(false); 182 return m_database.GetSetState(instanceIdentifier); 183 } 184 185 clock::time_point ConfigurationStatus::GetSetFirstApply(const winrt::guid& instanceIdentifier) 186 { 187 m_database.EnsureOpened(false); 188 return clock::from_sys(m_database.GetSetFirstApply(instanceIdentifier)); 189 } 190 191 clock::time_point ConfigurationStatus::GetSetApplyBegun(const winrt::guid& instanceIdentifier) 192 { 193 using system_clock = std::chrono::system_clock; 194 195 m_database.EnsureOpened(false); 196 system_clock::time_point result = m_database.GetSetApplyBegun(instanceIdentifier); 197 return (result == system_clock::time_point{} ? clock::time_point{} : clock::from_sys(result)); 198 } 199 200 clock::time_point ConfigurationStatus::GetSetApplyEnded(const winrt::guid& instanceIdentifier) 201 { 202 using system_clock = std::chrono::system_clock; 203 204 m_database.EnsureOpened(false); 205 system_clock::time_point result = m_database.GetSetApplyEnded(instanceIdentifier); 206 return (result == system_clock::time_point{} ? clock::time_point{} : clock::from_sys(result)); 207 } 208 209 ConfigurationUnitState ConfigurationStatus::GetUnitState(const winrt::guid& instanceIdentifier) 210 { 211 m_database.EnsureOpened(false); 212 return m_database.GetUnitState(instanceIdentifier); 213 } 214 215 IConfigurationUnitResultInformation ConfigurationStatus::GetUnitResultInformation(const winrt::guid& instanceIdentifier) 216 { 217 m_database.EnsureOpened(false); 218 return m_database.GetUnitResultInformation(instanceIdentifier); 219 } 220 221 void ConfigurationStatus::UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state) 222 { 223 m_database.EnsureOpened(); 224 m_database.UpdateSetState(setInstanceIdentifier, state); 225 SignalChangeListeners(); 226 } 227 228 void ConfigurationStatus::UpdateSetState(const guid& setInstanceIdentifier, bool inQueue) 229 { 230 m_database.EnsureOpened(); 231 m_database.UpdateSetInQueue(setInstanceIdentifier, inQueue); 232 SignalChangeListeners(); 233 } 234 235 void ConfigurationStatus::UpdateUnitState(const guid& setInstanceIdentifier, const com_ptr<implementation::ConfigurationSetChangeData>& changeData) 236 { 237 m_database.EnsureOpened(); 238 m_database.UpdateUnitState(setInstanceIdentifier, changeData); 239 SignalChangeListeners(); 240 } 241 242 ConfigurationStatus::SetChangeRegistration::SetChangeRegistration(const winrt::guid& instanceIdentifier, ConfigurationSet* configurationSet) : 243 m_status(Instance()), m_instanceIdentifier(instanceIdentifier), m_configurationSet(configurationSet) {} 244 245 ConfigurationStatus::SetChangeRegistration::~SetChangeRegistration() 246 { 247 m_status->RemoveSetChangeRegistration(m_instanceIdentifier, m_configurationSet); 248 } 249 250 std::shared_ptr<ConfigurationStatus::SetChangeRegistration> ConfigurationStatus::RegisterForSetChange(ConfigurationSet& set) 251 { 252 m_database.EnsureOpened(); 253 254 winrt::guid instanceIdentifier = set.InstanceIdentifier(); 255 256 { 257 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 258 m_setChangeRegistrations.emplace(instanceIdentifier, &set); 259 EnableChangeListeningIfNeeded(); 260 } 261 262 return std::make_shared<SetChangeRegistration>(instanceIdentifier, &set); 263 } 264 265 void ConfigurationStatus::RemoveSetChangeRegistration(const winrt::guid& instanceIdentifier, ConfigurationSet* configurationSet) noexcept 266 { 267 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 268 269 auto [begin, end] = m_setChangeRegistrations.equal_range(instanceIdentifier); 270 271 for (; begin != end; ++begin) 272 { 273 if (begin->second == configurationSet) 274 { 275 m_setChangeRegistrations.erase(begin); 276 break; 277 } 278 } 279 280 DisableChangeListeningIfNeeded(); 281 } 282 283 ConfigurationStatus::ChangeRegistration::ChangeRegistration(const winrt::guid& instanceIdentifier) : 284 m_status(Instance()), m_instanceIdentifier(instanceIdentifier) {} 285 286 ConfigurationStatus::ChangeRegistration::~ChangeRegistration() 287 { 288 m_status->RemoveChangeRegistration(m_instanceIdentifier); 289 } 290 291 std::shared_ptr<ConfigurationStatus::ChangeRegistration> ConfigurationStatus::RegisterForChange(ConfigurationProcessor& processor) 292 { 293 m_database.EnsureOpened(); 294 295 GUID instanceIdentifier; 296 std::ignore = CoCreateGuid(&instanceIdentifier); 297 298 { 299 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 300 m_changeRegistrations.emplace_back(instanceIdentifier, &processor); 301 EnableChangeListeningIfNeeded(); 302 } 303 304 return std::make_shared<ChangeRegistration>(instanceIdentifier); 305 } 306 307 void ConfigurationStatus::RemoveChangeRegistration(const winrt::guid& instanceIdentifier) noexcept 308 { 309 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 310 311 for (auto itr = m_changeRegistrations.begin(); itr != m_changeRegistrations.end(); ++itr) 312 { 313 if (itr->first == instanceIdentifier) 314 { 315 m_changeRegistrations.erase(itr); 316 DisableChangeListeningIfNeeded(); 317 return; 318 } 319 } 320 } 321 322 void ConfigurationStatus::EnableChangeListeningIfNeeded() 323 { 324 if (!m_changeListener) 325 { 326 m_changeListener = std::make_unique<details::ChangeListener>(*this); 327 } 328 } 329 330 void ConfigurationStatus::DisableChangeListeningIfNeeded() 331 { 332 if (m_changeListener && m_setChangeRegistrations.empty() && m_changeRegistrations.empty()) 333 { 334 m_changeListener.reset(); 335 } 336 } 337 338 void ConfigurationStatus::SignalChangeListeners() 339 { 340 std::vector<ConfigurationDatabase::StatusChangeListener> changeListeners = m_database.GetChangeListeners(); 341 342 for (const auto& listener : changeListeners) 343 { 344 std::wstring objectName = AppInstaller::Utility::ConvertToUTF16(listener.ObjectName); 345 wil::unique_event listenerEvent; 346 if (listenerEvent.try_open(objectName.c_str(), EVENT_MODIFY_STATE)) 347 { 348 listenerEvent.SetEvent(); 349 } 350 else 351 { 352 m_database.RemoveListener(listener.ObjectName); 353 } 354 } 355 } 356 357 ConfigurationDatabase& ConfigurationStatus::Database() 358 { 359 return m_database; 360 } 361 362 bool ConfigurationStatus::HasSetChangeRegistration(const guid& setInstanceIdentifier) 363 { 364 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 365 auto [begin, end] = m_setChangeRegistrations.equal_range(setInstanceIdentifier); 366 return begin != end; 367 } 368 369 bool ConfigurationStatus::HasChangeRegistrations() 370 { 371 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 372 return !m_changeRegistrations.empty(); 373 } 374 375 void ConfigurationStatus::SetChangeDetected(const winrt::guid& setInstanceIdentifier, com_ptr<ConfigurationSetChangeData>& data, const std::optional<GUID>& unitInstanceIdentifier) 376 { 377 std::vector<ConfigurationSet*> setChangeRegistrations; 378 379 { 380 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 381 382 auto [begin, end] = m_setChangeRegistrations.equal_range(setInstanceIdentifier); 383 384 for (; begin != end; ++begin) 385 { 386 setChangeRegistrations.emplace_back(begin->second); 387 } 388 } 389 390 for (ConfigurationSet* set : setChangeRegistrations) 391 { 392 set->ConfigurationSetChange(data, unitInstanceIdentifier); 393 } 394 } 395 396 void ConfigurationStatus::ChangeDetected(const Configuration::ConfigurationSet& set, const Configuration::ConfigurationChangeData& data) 397 { 398 std::vector<std::pair<winrt::guid, ConfigurationProcessor*>> changeRegistrations; 399 400 { 401 std::lock_guard<std::mutex> lock{ m_changeRegistrationsMutex }; 402 changeRegistrations = m_changeRegistrations; 403 } 404 405 for (const auto& registration : changeRegistrations) 406 { 407 registration.second->ConfigurationChange(set, data); 408 } 409 } 410 }