winget-cli

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

ExecutionContext.cpp (9216B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "AppInstallerRuntime.h"
      5 #include "Argument.h"
      6 #include "COMContext.h"
      7 #include "Command.h"
      8 #include "ExecutionContext.h"
      9 #include "Public/ShutdownMonitoring.h"
     10 #include <winget/Checkpoint.h>
     11 #include <winget/Reboot.h>
     12 #include <winget/UserSettings.h>
     13 #include <winget/NetworkSettings.h>
     14 
     15 using namespace AppInstaller::Checkpoints;
     16 
     17 namespace AppInstaller::CLI::Execution
     18 {
     19     using namespace Settings;
     20 
     21     namespace
     22     {
     23         bool ShouldRemoveCheckpointDatabase(HRESULT hr)
     24         {
     25             switch (hr)
     26             {
     27             case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL:
     28             case APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED:
     29             case APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH:
     30                 return false;
     31             default:
     32                 return true;
     33             }
     34         }
     35     }
     36 
     37     Context::~Context()
     38     {
     39         if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume))
     40         {
     41             if (m_checkpointManager && (!IsTerminated() || ShouldRemoveCheckpointDatabase(GetTerminationHR())))
     42             {
     43                 m_checkpointManager->CleanUpDatabase();
     44                 AppInstaller::Reboot::UnregisterRestartForWER();
     45             }
     46         }
     47 
     48         if (m_disableSignalTerminationHandlerOnExit)
     49         {
     50             EnableSignalTerminationHandler(false);
     51         }
     52     }
     53 
     54     Context Context::CreateEmptyContext()
     55     {
     56         AppInstaller::ThreadLocalStorage::WingetThreadGlobals threadGlobals;
     57         return Context(Reporter, threadGlobals);
     58     }
     59 
     60     std::unique_ptr<Context> Context::CreateSubContext()
     61     {
     62         auto clone = std::make_unique<Context>(Reporter, m_threadGlobals);
     63         clone->m_flags = m_flags;
     64         clone->m_executingCommand = m_executingCommand;
     65         // If the parent is hooked up to the CTRL signal, have the clone be as well
     66         if (m_disableSignalTerminationHandlerOnExit)
     67         {
     68             clone->EnableSignalTerminationHandler();
     69         }
     70         CopyArgsToSubContext(clone.get());
     71         CopyDataToSubContext(clone.get());
     72         return clone;
     73     }
     74 
     75     void Context::CopyArgsToSubContext(Context* subContext)
     76     {
     77         auto argProperties = ArgumentCommon::GetFromExecArgs(Args);
     78         for (const auto& arg : argProperties)
     79         {
     80             if (WI_IsFlagSet(arg.TypeCategory, ArgTypeCategory::CopyFlagToSubContext))
     81             {
     82                 subContext->Args.AddArg(arg.Type);
     83             }
     84             else if (WI_IsFlagSet(arg.TypeCategory, ArgTypeCategory::CopyValueToSubContext))
     85             {
     86                 subContext->Args.AddArg(arg.Type, Args.GetArg(arg.Type));
     87             }
     88         }
     89     }
     90 
     91     void Context::CopyDataToSubContext(Context* subContext)
     92     {
     93 #define COPY_DATA_IF_EXISTS(dataType) \
     94         if (this->Contains(dataType)) \
     95         { \
     96             subContext->Add<dataType>(this->Get<dataType>()); \
     97         }
     98 
     99         COPY_DATA_IF_EXISTS(Data::InstallerDownloadAuthenticators);
    100     }
    101 
    102     void Context::EnableSignalTerminationHandler(bool enabled)
    103     {
    104         ShutdownMonitoring::TerminationSignalHandler::EnableListener(enabled, this);
    105         m_disableSignalTerminationHandlerOnExit = enabled;
    106     }
    107 
    108     void Context::UpdateForArgs()
    109     {
    110         // Change logging level to Info if Verbose not requested
    111         if (Args.Contains(Args::Type::VerboseLogs))
    112         {
    113             Logging::Log().SetLevel(Logging::Level::Verbose);
    114         }
    115 
    116         // Disable warnings if requested
    117         if (Args.Contains(Args::Type::IgnoreWarnings))
    118         {
    119             Reporter.SetLevelMask(Reporter::Level::Warning, false);
    120         }
    121 
    122         // Set proxy
    123         if (Args.Contains(Args::Type::Proxy))
    124         {
    125             Network().SetProxyUri(std::string{ Args.GetArg(Args::Type::Proxy) });
    126         }
    127         else if (Args.Contains(Args::Type::NoProxy))
    128         {
    129             Network().SetProxyUri(std::nullopt);
    130         }
    131 
    132         // Set visual style
    133         if (Args.Contains(Args::Type::NoVT))
    134         {
    135             Reporter.SetStyle(VisualStyle::NoVT);
    136         }
    137         else if (Args.Contains(Args::Type::RetroStyle))
    138         {
    139             Reporter.SetStyle(VisualStyle::Retro);
    140         }
    141         else if (Args.Contains(Args::Type::RainbowStyle))
    142         {
    143             Reporter.SetStyle(VisualStyle::Rainbow);
    144         }
    145         else
    146         {
    147             Reporter.SetStyle(User().Get<Setting::ProgressBarVisualStyle>());
    148         }
    149     }
    150 
    151     void Context::Terminate(HRESULT hr, std::string_view file, size_t line)
    152     {
    153         if (hr == APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED)
    154         {
    155             ++m_CtrlSignalCount;
    156             // Use a more recognizable error
    157             hr = E_ABORT;
    158 
    159             // If things aren't terminating fast enough for the user, they will probably press CTRL+C again.
    160             // In that case, we should forcibly terminate.
    161             // Unless we want to spin a separate thread for all work, we have to just exit here.
    162             if (m_CtrlSignalCount >= 2)
    163             {
    164                 Reporter.CloseOutputStream(true);
    165                 Logging::Telemetry().LogCommandTermination(hr, file, line);
    166                 std::exit(hr);
    167             }
    168         }
    169         else if (hr == APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED)
    170         {
    171             AICLI_LOG(CLI, Info, << "Got app termination signal");
    172             hr = E_ABORT;
    173         }
    174 
    175         Logging::Telemetry().LogCommandTermination(hr, file, line);
    176 
    177         if (!m_isTerminated)
    178         {
    179             SetTerminationHR(hr);
    180         }
    181     }
    182 
    183     void Context::SetTerminationHR(HRESULT hr)
    184     {
    185         m_terminationHR = hr;
    186         m_isTerminated = true;
    187     }
    188 
    189     void Context::Cancel(CancelReason reason, bool bypassUser)
    190     {
    191         HRESULT hr = ToHRESULT(reason);
    192 
    193         Terminate(hr);
    194         Reporter.CancelInProgressTask(bypassUser, reason);
    195     }
    196 
    197     void Context::SetExecutionStage(Workflow::ExecutionStage stage)
    198     {
    199         if (m_executionStage == stage)
    200         {
    201             return;
    202         }
    203         else if (m_executionStage > stage)
    204         {
    205             THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), "Reporting ExecutionStage to an earlier Stage: current[%d], new[%d]", ToIntegral(m_executionStage), ToIntegral(stage));
    206         }
    207 
    208         m_executionStage = stage;
    209         GetThreadGlobals().GetTelemetryLogger().SetExecutionStage(static_cast<uint32_t>(m_executionStage));
    210     }
    211 
    212     AppInstaller::ThreadLocalStorage::WingetThreadGlobals& Context::GetThreadGlobals()
    213     {
    214         return m_threadGlobals;
    215     }
    216 
    217     std::unique_ptr<AppInstaller::ThreadLocalStorage::PreviousThreadGlobals> Context::SetForCurrentThread()
    218     {
    219         return m_threadGlobals.SetForCurrentThread();
    220     }
    221 
    222 #ifndef AICLI_DISABLE_TEST_HOOKS
    223     bool Context::ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task)
    224     {
    225         return (m_shouldExecuteWorkflowTask ? m_shouldExecuteWorkflowTask(task) : true);
    226     }
    227 #endif
    228 
    229     void ContextEnumBasedVariantMapActionCallback(const void* map, Data data, EnumBasedVariantMapAction action)
    230     {
    231         switch (action)
    232         {
    233         case EnumBasedVariantMapAction::Add:
    234             AICLI_LOG(Workflow, Verbose, << "Setting data item: " << data);
    235             break;
    236         case EnumBasedVariantMapAction::Contains:
    237             AICLI_LOG(Workflow, Verbose, << "Checking data item: " << data);
    238             break;
    239         case EnumBasedVariantMapAction::Get:
    240             AICLI_LOG(Workflow, Verbose, << "Getting data item: " << data);
    241             break;
    242         }
    243 
    244         UNREFERENCED_PARAMETER(map);
    245     }
    246 
    247     std::string Context::GetResumeId()
    248     {
    249         return m_checkpointManager->GetResumeId();
    250     }
    251 
    252     std::optional<Checkpoint<AutomaticCheckpointData>> Context::LoadCheckpoint(const std::string& resumeId)
    253     {
    254         m_checkpointManager = std::make_unique<AppInstaller::Checkpoints::CheckpointManager>(resumeId);
    255         return m_checkpointManager->GetAutomaticCheckpoint();
    256     }
    257 
    258     std::vector<AppInstaller::Checkpoints::Checkpoint<Execution::Data>> Context::GetCheckpoints()
    259     {
    260         return m_checkpointManager->GetCheckpoints();
    261     }
    262 
    263     void Context::Checkpoint(std::string_view checkpointName, std::vector<Execution::Data> contextData)
    264     {
    265         UNREFERENCED_PARAMETER(checkpointName);
    266         UNREFERENCED_PARAMETER(contextData);
    267 
    268         if (!m_checkpointManager)
    269         {
    270             m_checkpointManager = std::make_unique<AppInstaller::Checkpoints::CheckpointManager>();
    271             m_checkpointManager->CreateAutomaticCheckpoint(*this);
    272 
    273             // Register for restart only when we first call checkpoint to support restarting from an unexpected shutdown.
    274             AppInstaller::Reboot::RegisterRestartForWER("resume -g " + GetResumeId());
    275         }
    276 
    277         // TODO: Capture context data for checkpoint.
    278     }
    279 }