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 }