winget-cli

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

ExecutionContext.h (9432B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #pragma once
      4 #include "winget/ThreadGlobals.h"
      5 #include "ExecutionReporter.h"
      6 #include "ExecutionArgs.h"
      7 #include "ExecutionContextData.h"
      8 #include "CompletionData.h"
      9 #include "CheckpointManager.h"
     10 #include <AppInstallerProgress.h>
     11 #include <winget/Checkpoint.h>
     12 
     13 #include <string_view>
     14 
     15 #define WINGET_CATCH_RESULT_EXCEPTION_STORE(exceptionHR)   catch (const wil::ResultException& re) { exceptionHR = re.GetErrorCode(); }
     16 #define WINGET_CATCH_HRESULT_EXCEPTION_STORE(exceptionHR)   catch (const winrt::hresult_error& hre) { exceptionHR = hre.code(); }
     17 #define WINGET_CATCH_COMMAND_EXCEPTION_STORE(exceptionHR)   catch (const ::AppInstaller::CLI::CommandException&) { exceptionHR = APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS; }
     18 #define WINGET_CATCH_POLICY_EXCEPTION_STORE(exceptionHR)   catch (const ::AppInstaller::Settings::GroupPolicyException&) { exceptionHR = APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; }
     19 #define WINGET_CATCH_STD_EXCEPTION_STORE(exceptionHR, genericHR)   catch (const std::exception&) { exceptionHR = genericHR; }
     20 #define WINGET_CATCH_ALL_EXCEPTION_STORE(exceptionHR, genericHR)   catch (...) { exceptionHR = genericHR; }
     21 #define WINGET_CATCH_STORE(exceptionHR, genericHR) \
     22         WINGET_CATCH_RESULT_EXCEPTION_STORE(exceptionHR) \
     23         WINGET_CATCH_HRESULT_EXCEPTION_STORE(exceptionHR) \
     24         WINGET_CATCH_COMMAND_EXCEPTION_STORE(exceptionHR) \
     25         WINGET_CATCH_POLICY_EXCEPTION_STORE(exceptionHR) \
     26         WINGET_CATCH_STD_EXCEPTION_STORE(exceptionHR, genericHR) \
     27         WINGET_CATCH_ALL_EXCEPTION_STORE(exceptionHR, genericHR)
     28 
     29 // Terminates the Context with some logging to indicate the location.
     30 // Also returns from the current function.
     31 #define AICLI_TERMINATE_CONTEXT_ARGS(_context_,_hr_,_ret_) \
     32     do { \
     33         _context_.Terminate(_hr_, __FILE__, __LINE__); \
     34         return _ret_; \
     35     } while(0,0)
     36 
     37 // Terminates the Context named 'context' with some logging to indicate the location.
     38 // Also returns from the current function.
     39 #define AICLI_TERMINATE_CONTEXT(_hr_)   AICLI_TERMINATE_CONTEXT_ARGS(context,_hr_,)
     40 
     41 // Terminates the Context named 'context' with some logging to indicate the location.
     42 // Also returns the specified value from the current function.
     43 #define AICLI_TERMINATE_CONTEXT_RETURN(_hr_,_ret_) AICLI_TERMINATE_CONTEXT_ARGS(context,_hr_,_ret_)
     44 
     45 // Returns if the context is terminated.
     46 #define AICLI_RETURN_IF_TERMINATED(_context_) if ((_context_).IsTerminated()) { return; }
     47 
     48 namespace AppInstaller::CLI
     49 {
     50     struct Command;
     51 }
     52 
     53 namespace AppInstaller::CLI::Workflow
     54 {
     55     struct WorkflowTask;
     56     enum class ExecutionStage : uint32_t;
     57 }
     58 
     59 namespace AppInstaller::CLI::Execution
     60 {
     61     // bit masks used as Context flags
     62     enum class ContextFlag : int
     63     {
     64         None = 0x0,
     65         InstallerExecutionUseUpdate = 0x1,
     66         InstallerHashMatched = 0x2,
     67         InstallerTrusted = 0x4,
     68         // Allows a failure in a single source to generate a warning rather than an error.
     69         // TODO: Remove when the source interface is refactored.
     70         TreatSourceFailuresAsWarning = 0x8,
     71         ShowSearchResultsOnPartialFailure = 0x10,
     72         DisableInteractivity = 0x40,
     73         BypassIsStoreClientBlockedPolicyCheck = 0x80,
     74         InstallerDownloadOnly = 0x100,
     75         Resume = 0x200,
     76         RebootRequired = 0x400,
     77         RegisterResume = 0x800,
     78         InstallerExecutionUseRepair = 0x1000,
     79     };
     80 
     81     DEFINE_ENUM_FLAG_OPERATORS(ContextFlag);
     82 
     83 #ifndef AICLI_DISABLE_TEST_HOOKS
     84     HWND GetWindowHandle();
     85 
     86     bool WaitForAppShutdownEvent();
     87 #endif
     88 
     89     // Callback to log data actions.
     90     void ContextEnumBasedVariantMapActionCallback(const void* map, Data data, EnumBasedVariantMapAction action);
     91 
     92     // The context within which all commands execute.
     93     // Contains input/output via Execution::Reporter and
     94     // arguments via Execution::Args.
     95     struct Context : EnumBasedVariantMap<Data, details::DataMapping, ContextEnumBasedVariantMapActionCallback>, ICancellable
     96     {
     97         Context() = default;
     98         Context(std::ostream& out, std::istream& in) : Reporter(out, in) {}
     99 
    100         // Constructor for creating a sub-context.
    101         Context(Execution::Reporter& reporter, ThreadLocalStorage::WingetThreadGlobals& threadGlobals) :
    102             Reporter(reporter, Execution::Reporter::clone_t{}),
    103             m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) {}
    104 
    105         virtual ~Context();
    106 
    107         // The path for console input/output for all functionality.
    108         Reporter Reporter;
    109 
    110         // The arguments given to execute with.
    111         Args Args;
    112 
    113         // Creates a empty context, inheriting 
    114         Context CreateEmptyContext();
    115 
    116         // Creates a child of this context.
    117         virtual std::unique_ptr<Context> CreateSubContext();
    118 
    119         // Enables reception of CTRL signals and window messages.
    120         void EnableSignalTerminationHandler(bool enabled = true);
    121 
    122         // Applies changes based on the parsed args.
    123         void UpdateForArgs();
    124 
    125         // Returns a value indicating whether the context is terminated.
    126         bool IsTerminated() const { return m_isTerminated; }
    127 
    128         // Resets the context to a nonterminated state. 
    129         void ResetTermination() { m_terminationHR = S_OK; m_isTerminated = false; }
    130 
    131         // Gets the HRESULT reason for the termination.
    132         HRESULT GetTerminationHR() const { return m_terminationHR; }
    133 
    134         // Set the context to the terminated state.
    135         void Terminate(HRESULT hr, std::string_view file = {}, size_t line = {});
    136 
    137         // Set the termination hr of the context.
    138         void SetTerminationHR(HRESULT hr);
    139 
    140         // Cancel the context; this terminates it as well as informing any in progress task to stop cooperatively.
    141         // Multiple attempts with CancelReason::CancelSignal may cause the process to simply exit.
    142         // The bypassUser indicates whether the user should be asked for cancellation (does not currently have any effect).
    143         void Cancel(CancelReason reason, bool bypassUser = false) override;
    144 
    145         // Gets context flags
    146         ContextFlag GetFlags() const
    147         {
    148             return m_flags;
    149         }
    150 
    151         // Set context flags
    152         void SetFlags(ContextFlag flags)
    153         {
    154             WI_SetAllFlags(m_flags, flags);
    155         }
    156 
    157         // Clear context flags
    158         void ClearFlags(ContextFlag flags)
    159         {
    160             WI_ClearAllFlags(m_flags, flags);
    161         }
    162 
    163         virtual void SetExecutionStage(Workflow::ExecutionStage stage);
    164 
    165         // Get Globals for Current Context
    166         AppInstaller::ThreadLocalStorage::WingetThreadGlobals& GetThreadGlobals();
    167 
    168         std::unique_ptr<AppInstaller::ThreadLocalStorage::PreviousThreadGlobals> SetForCurrentThread();
    169 
    170         // Gets the executing command
    171         AppInstaller::CLI::Command* GetExecutingCommand() { return m_executingCommand; }
    172 
    173         // Sets the executing command
    174         void SetExecutingCommand(AppInstaller::CLI::Command* command) { m_executingCommand = command; }
    175 
    176 #ifndef AICLI_DISABLE_TEST_HOOKS
    177         // Enable tests to override behavior
    178         bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task);
    179 #endif
    180 
    181         // Returns the resume id.
    182         std::string GetResumeId();
    183 
    184         // Called by the resume command. Loads the checkpoint manager with the resume id and returns the automatic checkpoint.
    185         std::optional<AppInstaller::Checkpoints::Checkpoint<AppInstaller::Checkpoints::AutomaticCheckpointData>> LoadCheckpoint(const std::string& resumeId);
    186 
    187         // Returns data checkpoints in the order of latest checkpoint to earliest.
    188         std::vector<AppInstaller::Checkpoints::Checkpoint<Execution::Data>> GetCheckpoints();
    189 
    190         // Creates a checkpoint for the provided context data.
    191         void Checkpoint(std::string_view checkpointName, std::vector<Execution::Data> contextData);
    192 
    193     protected:
    194         // Copies the args that are also needed in a sub-context. E.g., silent
    195         void CopyArgsToSubContext(Context* subContext);
    196 
    197         // Copies the execution data that are also needed in a sub-context. E.g., shared installer download authenticator map
    198         void CopyDataToSubContext(Context* subContext);
    199 
    200         // Neither virtual functions nor member fields can be inside AICLI_DISABLE_TEST_HOOKS
    201         // or we could have ODR violations that lead to nasty bugs. So we will simply never
    202         // use this if AICLI_DISABLE_TEST_HOOKS is defined.
    203         std::function<bool(const Workflow::WorkflowTask&)> m_shouldExecuteWorkflowTask;
    204 
    205     private:
    206         DestructionToken m_disableSignalTerminationHandlerOnExit = false;
    207         bool m_isTerminated = false;
    208         HRESULT m_terminationHR = S_OK;
    209         size_t m_CtrlSignalCount = 0;
    210         ContextFlag m_flags = ContextFlag::None;
    211         Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial;
    212         AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals;
    213         AppInstaller::CLI::Command* m_executingCommand = nullptr;
    214         std::unique_ptr<AppInstaller::Checkpoints::CheckpointManager> m_checkpointManager;
    215     };
    216 }