winget-cli

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

Core.cpp (9110B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/AppInstallerCLICore.h"
      5 #include "Commands/RootCommand.h"
      6 #include "ExecutionContext.h"
      7 #include "Workflows/WorkflowBase.h"
      8 #include <winget/UserSettings.h>
      9 #include "Commands/InstallCommand.h"
     10 #include "COMContext.h"
     11 #include <AppInstallerFileLogger.h>
     12 #include <winget/OutputDebugStringLogger.h>
     13 #include "Public/ShutdownMonitoring.h"
     14 
     15 #ifndef AICLI_DISABLE_TEST_HOOKS
     16 #include <winget/Debugging.h>
     17 #endif
     18 
     19 using namespace winrt;
     20 using namespace winrt::Windows::Foundation;
     21 using namespace AppInstaller::CLI;
     22 using namespace AppInstaller::Utility::literals;
     23 
     24 namespace AppInstaller::CLI
     25 {
     26     namespace
     27     {
     28         // RAII class to restore the console output codepage.
     29         struct ConsoleOutputCPRestore
     30         {
     31             ConsoleOutputCPRestore(UINT cpToChangeTo)
     32             {
     33                 m_previousCP = GetConsoleOutputCP();
     34                 LOG_LAST_ERROR_IF(!SetConsoleOutputCP(cpToChangeTo));
     35             }
     36 
     37             ~ConsoleOutputCPRestore()
     38             {
     39                 SetConsoleOutputCP(m_previousCP);
     40             }
     41 
     42             ConsoleOutputCPRestore(const ConsoleOutputCPRestore&) = delete;
     43             ConsoleOutputCPRestore& operator=(const ConsoleOutputCPRestore&) = delete;
     44 
     45             ConsoleOutputCPRestore(ConsoleOutputCPRestore&&) = delete;
     46             ConsoleOutputCPRestore& operator=(ConsoleOutputCPRestore&&) = delete;
     47 
     48         private:
     49             UINT m_previousCP = 0;
     50         };
     51 
     52         void __CRTDECL abort_signal_handler(int)
     53         {
     54 #ifndef AICLI_DISABLE_TEST_HOOKS
     55             if (Settings::User().Get<Settings::Setting::EnableSelfInitiatedMinidump>())
     56             {
     57                 Debugging::WriteMinidump();
     58             }
     59 #endif
     60 
     61             std::_Exit(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR);
     62         }
     63 
     64         wil::slim_event_manual_reset& GetMainWaitEvent()
     65         {
     66             static wil::slim_event_manual_reset s_mainWait;
     67             return s_mainWait;
     68         }
     69 
     70         void WaitOnMainWaitEvent()
     71         {
     72             GetMainWaitEvent().wait(5000);
     73         }
     74 
     75         void RegisterShutdownBlocker()
     76         {
     77             ShutdownMonitoring::ServerShutdownSynchronization::ComponentSystem main{};
     78             main.Wait = WaitOnMainWaitEvent;
     79             ShutdownMonitoring::ServerShutdownSynchronization::AddComponent(main);
     80         }
     81     }
     82 
     83     int CoreMain(int argc, wchar_t const** argv) try
     84     {
     85         // This prevents the OS package management from terminating the CLI process before it has had a chance to gracefully exit.
     86         RegisterShutdownBlocker();
     87         auto signalMainExit = wil::scope_exit([]() { GetMainWaitEvent().SetEvent(); });
     88 
     89         std::signal(SIGABRT, abort_signal_handler);
     90 
     91         init_apartment();
     92 
     93 #ifndef AICLI_DISABLE_TEST_HOOKS
     94         // We have to do this here so the auto minidump config initialization gets caught
     95         Logging::OutputDebugStringLogger::Add();
     96         Logging::Log().SetEnabledChannels(Logging::Channel::All);
     97         Logging::Log().SetLevel(Logging::Level::Verbose);
     98 
     99         if (Settings::User().Get<Settings::Setting::EnableSelfInitiatedMinidump>())
    100         {
    101             Debugging::EnableSelfInitiatedMinidump();
    102         }
    103 
    104         Logging::OutputDebugStringLogger::Remove();
    105 #endif
    106 
    107         Logging::UseGlobalTelemetryLoggerActivityIdOnly();
    108 
    109         Execution::Context context;
    110         auto previousThreadGlobals = context.SetForCurrentThread();
    111 
    112         // Set up debug string logging during initialization
    113         Logging::OutputDebugStringLogger::Add();
    114         Logging::Log().SetEnabledChannels(Logging::Channel::All);
    115         Logging::Log().SetLevel(Logging::Level::Verbose);
    116 
    117         Logging::Log().SetEnabledChannels(Settings::User().Get<Settings::Setting::LoggingChannelPreference>());
    118         Logging::Log().SetLevel(Settings::User().Get<Settings::Setting::LoggingLevelPreference>());
    119         Logging::FileLogger::Add();
    120         Logging::OutputDebugStringLogger::Remove();
    121         Logging::EnableWilFailureTelemetry();
    122 
    123         // Set output to UTF8
    124         ConsoleOutputCPRestore utf8CP(CP_UTF8);
    125 
    126         Logging::Telemetry().SetCaller("winget-cli");
    127         Logging::Telemetry().LogStartup();
    128 
    129 #ifndef AICLI_DISABLE_TEST_HOOKS
    130         if (!Settings::User().Get<Settings::Setting::KeepAllLogFiles>())
    131 #endif
    132         {
    133             // Initiate the background cleanup of the log file location.
    134             Logging::FileLogger::BeginCleanup();
    135         }
    136 
    137         context.EnableSignalTerminationHandler();
    138 
    139         context << Workflow::ReportExecutionStage(Workflow::ExecutionStage::ParseArgs);
    140 
    141         // Convert incoming wide char args to UTF8
    142         std::vector<std::string> utf8Args;
    143         for (int i = 1; i < argc; ++i)
    144         {
    145             utf8Args.emplace_back(Utility::ConvertToUTF8(argv[i]));
    146         }
    147 
    148         AICLI_LOG(CLI, Info, << "WinGet invoked with arguments:" << [&]() {
    149                 std::stringstream strstr;
    150                 for (const auto& arg : utf8Args)
    151                 {
    152                     strstr << " '" << arg << '\'';
    153                 }
    154                 return strstr.str();
    155             }());
    156 
    157         Invocation invocation{ std::move(utf8Args) };
    158 
    159         // The root command is our fallback in the event of very bad or very little input
    160         std::unique_ptr<Command> command = std::make_unique<RootCommand>();
    161 
    162         try
    163         {
    164             // Block CLI execution if WinGetCommandLineInterfaces is disabled by Policy
    165             if (!Settings::GroupPolicies().IsEnabled(Settings::TogglePolicy::Policy::WinGetCommandLineInterfaces))
    166             {
    167                 AICLI_LOG(CLI, Error, << "WinGet is disabled by group policy");
    168                 throw Settings::GroupPolicyException(Settings::TogglePolicy::Policy::WinGetCommandLineInterfaces);
    169             }
    170 
    171             std::unique_ptr<Command> subCommand = command->FindSubCommand(invocation);
    172             while (subCommand)
    173             {
    174                 command = std::move(subCommand);
    175                 subCommand = command->FindSubCommand(invocation);
    176             }
    177             Logging::Telemetry().LogCommand(command->FullName());
    178 
    179             command->ParseArguments(invocation, context.Args);
    180             context.UpdateForArgs();
    181             context.SetExecutingCommand(command.get());
    182             command->ValidateArguments(context.Args);
    183         }
    184         // Exceptions specific to parsing the arguments of a command
    185         catch (const CommandException& ce)
    186         {
    187             command->OutputHelp(context.Reporter, &ce);
    188             AICLI_LOG(CLI, Error, << "Error encountered parsing command line: " << ce.Message());
    189             return APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS;
    190         }
    191         catch (const Settings::GroupPolicyException& e)
    192         {
    193             // Report any action blocked by Group Policy.
    194             auto policy = Settings::TogglePolicy::GetPolicy(e.Policy());
    195             AICLI_LOG(CLI, Error, << "Operation blocked by Group Policy: " << policy.RegValueName());
    196             context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl;
    197             return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY;
    198         }
    199         catch (...)
    200         {
    201             return Workflow::HandleException(context, std::current_exception());
    202         }
    203 
    204         return Execute(context, command);
    205     }
    206     // End of the line exceptions that are not ever expected.
    207     // Telemetry cannot be reliable beyond this point, so don't let these happen.
    208     catch (...)
    209     {
    210         return APPINSTALLER_CLI_ERROR_INTERNAL_ERROR;
    211     }
    212 
    213     void ServerInitialize()
    214     {
    215 #ifndef AICLI_DISABLE_TEST_HOOKS
    216         // We have to do this here so the auto minidump config initialization gets caught
    217         Logging::OutputDebugStringLogger::Add();
    218         Logging::Log().SetEnabledChannels(Logging::Channel::All);
    219         Logging::Log().SetLevel(Logging::Level::Verbose);
    220 
    221         if (Settings::User().Get<Settings::Setting::EnableSelfInitiatedMinidump>())
    222         {
    223             Debugging::EnableSelfInitiatedMinidump();
    224         }
    225 
    226         Logging::OutputDebugStringLogger::Remove();
    227 #endif
    228 
    229         AppInstaller::CLI::Execution::COMContext::SetLoggers();
    230     }
    231 
    232     void InProcInitialize()
    233     {
    234 #ifndef AICLI_DISABLE_TEST_HOOKS
    235         // We have to do this here so the auto minidump config initialization gets caught
    236         Logging::OutputDebugStringLogger::Add();
    237         Logging::Log().SetEnabledChannels(Logging::Channel::All);
    238         Logging::Log().SetLevel(Logging::Level::Verbose);
    239 
    240         if (Settings::User().Get<Settings::Setting::EnableSelfInitiatedMinidump>())
    241         {
    242             Debugging::EnableSelfInitiatedMinidump();
    243         }
    244 
    245         Logging::OutputDebugStringLogger::Remove();
    246 #endif
    247 
    248         // Explicitly set default channel and level before user settings from PackageManagerSettings
    249         AppInstaller::CLI::Execution::COMContext::SetLoggers(AppInstaller::Logging::Channel::Defaults, AppInstaller::Logging::Level::Info);
    250     }
    251 }