winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }