| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This app is written as a windowsless win32 app instead of a console app so |
| // that the app can be made entireless silent, as required by omaha. |
| |
| #include <Windows.h> |
| #include <shlobj.h> // Needed for IsUserAnAdmin() |
| |
| #include <stdlib.h> |
| #include <string> |
| |
| #include "base/at_exit.h" |
| #include "base/command_line.h" |
| #include "base/cxx17_backports.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/process/memory.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/atl.h" |
| #include "base/win/process_startup_helper.h" |
| #include "base/win/scoped_com_initializer.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/win_util.h" |
| #include "base/win/windows_version.h" |
| #include "chrome/common/chrome_version.h" |
| #include "chrome/credential_provider/eventlog/gcp_eventlog_messages.h" |
| #include "chrome/credential_provider/gaiacp/gcp_utils.h" |
| #include "chrome/credential_provider/gaiacp/logging.h" |
| #include "chrome/credential_provider/gaiacp/reg_utils.h" |
| #include "chrome/credential_provider/setup/gcp_installer_crash_reporting.h" |
| #include "chrome/credential_provider/setup/setup_lib.h" |
| #include "chrome/credential_provider/setup/setup_utils.h" |
| #include "components/crash/core/app/crash_switches.h" |
| #include "components/crash/core/app/run_as_crashpad_handler_win.h" |
| #include "content/public/common/content_switches.h" |
| |
| using credential_provider::GetGlobalFlagOrDefault; |
| using credential_provider::kRegEnableVerboseLogging; |
| using credential_provider::MakeGcpwDefaultCP; |
| using credential_provider::putHR; |
| |
| namespace { |
| |
| bool IsPerUserInstallFromGoogleUpdate() { |
| wchar_t value[2]; |
| DWORD length = ::GetEnvironmentVariable(L"GoogleUpdateIsMachine", value, |
| std::size(value)); |
| |
| return length == 1 && value[0] == L'0'; |
| } |
| |
| } // namespace |
| |
| int APIENTRY wWinMain(HINSTANCE hInstance, |
| HINSTANCE /*hPrevInstance*/, |
| wchar_t* lpCmdLine, |
| int /*nCmdShow*/) { |
| HRESULT hr = S_OK; |
| |
| // Initialize base. Command line will be set from GetCommandLineW(). |
| base::AtExitManager exit_manager; |
| base::CommandLine::Init(0, nullptr); |
| |
| base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
| |
| std::string process_type = |
| cmdline->GetSwitchValueASCII(switches::kProcessType); |
| |
| if (process_type == crash_reporter::switches::kCrashpadHandler) { |
| return crash_reporter::RunAsCrashpadHandler(*cmdline, base::FilePath(), |
| switches::kProcessType, ""); |
| } |
| |
| // Initialize logging. |
| logging::LoggingSettings settings; |
| settings.logging_dest = logging::LOG_NONE; |
| |
| // See if the log file path was specified on the command line. |
| base::FilePath log_file_path = cmdline->GetSwitchValuePath("log-file"); |
| if (!log_file_path.empty()) { |
| settings.logging_dest = logging::LOG_TO_FILE; |
| settings.log_file_path = log_file_path.value().c_str(); |
| } |
| |
| logging::InitLogging(settings); |
| logging::SetLogItems(true, // Enable process id. |
| true, // Enable thread id. |
| true, // Enable timestamp. |
| false); // Enable tickcount. |
| |
| logging::SetEventSource("GCPW", GCPW_CATEGORY, MSG_LOG_MESSAGE); |
| |
| if (GetGlobalFlagOrDefault(kRegEnableVerboseLogging, 1)) |
| logging::SetMinLogLevel(logging::LOG_VERBOSE); |
| |
| // Set GCPW as the default credential provider for the end user. |
| MakeGcpwDefaultCP(); |
| |
| if (cmdline->HasSwitch(switches::kLoggingLevel)) { |
| std::string log_level = |
| cmdline->GetSwitchValueASCII(switches::kLoggingLevel); |
| int level = 0; |
| if (base::StringToInt(log_level, &level) && level >= 0 && |
| level < logging::LOGGING_NUM_SEVERITIES) { |
| logging::SetMinLogLevel(level); |
| } else { |
| LOGFN(WARNING) << "Bad log level: " << log_level; |
| } |
| } |
| |
| // Make sure the process exits cleanly on unexpected errors. |
| base::EnableTerminationOnHeapCorruption(); |
| base::EnableTerminationOnOutOfMemory(); |
| base::win::RegisterInvalidParamHandler(); |
| base::win::SetupCRT(*base::CommandLine::ForCurrentProcess()); |
| |
| if (!::IsUserAnAdmin()) { |
| LOGFN(ERROR) << "Setup must be run with administrative privilege."; |
| return -1; |
| } |
| |
| credential_provider::ConfigureGcpInstallerCrashReporting(*cmdline); |
| |
| // If the program is being run to either enable or disable stats, do that |
| // and exit. |
| if (cmdline->HasSwitch(credential_provider::switches::kEnableStats) || |
| cmdline->HasSwitch(credential_provider::switches::kDisableStats)) { |
| return credential_provider::EnableStatsCollection(*cmdline); |
| } |
| |
| base::FilePath gcp_setup_exe_path; |
| hr = credential_provider::GetPathToDllFromHandle(hInstance, |
| &gcp_setup_exe_path); |
| if (FAILED(hr)) { |
| LOGFN(ERROR) << "GetPathToDllFromHandle hr=" << putHR(hr); |
| return -1; |
| } |
| |
| wchar_t time_string[64]; |
| if (::GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, nullptr, nullptr, |
| time_string, std::size(time_string)) == 0) { |
| HRESULT last_error_hr = HRESULT_FROM_WIN32(::GetLastError()); |
| LOGFN(ERROR) << "GetTimeFormatEx(start) hr=" << putHR(last_error_hr); |
| wcscpy_s(time_string, std::size(time_string), L"Unknown"); |
| } |
| |
| LOGFN(INFO) << "Start: " << time_string; |
| LOGFN(INFO) << "Module: " << gcp_setup_exe_path; |
| LOGFN(INFO) << "Args: " << lpCmdLine; |
| LOGFN(INFO) << "Version: " << TEXT(CHROME_VERSION_STRING); |
| |
| LOGFN(INFO) << "Windows: " |
| << base::win::OSInfo::GetInstance()->Kernel32BaseVersion() |
| << " Version:" << credential_provider::GetWindowsVersion(); |
| |
| // If running from omaha, make sure machine install is used. |
| if (IsPerUserInstallFromGoogleUpdate()) { |
| LOGFN(ERROR) << "Only machine installs supported with Google Update"; |
| return -1; |
| } |
| |
| base::win::ScopedCOMInitializer com_initializer( |
| base::win::ScopedCOMInitializer::kMTA); |
| |
| // Parse command line. |
| bool is_uninstall = |
| cmdline->HasSwitch(credential_provider::switches::kUninstall); |
| base::FilePath path = |
| cmdline->GetSwitchValuePath(credential_provider::switches::kInstallPath); |
| std::string parent_handle_str = cmdline->GetSwitchValueASCII( |
| credential_provider::switches::kParentHandle); |
| |
| credential_provider::StandaloneInstallerConfigurator::Get() |
| ->ConfigureInstallationType(*cmdline); |
| |
| if (is_uninstall) { |
| // If this is a user invoked uninstall, copy the exe to the temp directory |
| // and rerun it from there. Append a new arg so that setup knows it is not |
| // user invoked and where to uninstall from. |
| if (path.empty()) { |
| hr = credential_provider::RelaunchUninstaller(gcp_setup_exe_path); |
| } else { |
| // Wait for parent process to exit. Proceed in any case. |
| if (!parent_handle_str.empty()) { |
| uint32_t parent_handle_value; |
| if (base::StringToUint(parent_handle_str, &parent_handle_value)) { |
| base::win::ScopedHandle parent_handle( |
| base::win::Uint32ToHandle(parent_handle_value)); |
| DWORD ret = ::WaitForSingleObject(parent_handle.Get(), 5000); |
| LOGFN(VERBOSE) << "Waited for parent(" << parent_handle.Get() |
| << "): ret=" << ret; |
| } |
| } |
| |
| hr = credential_provider::DoUninstall(gcp_setup_exe_path, path, nullptr); |
| |
| // Schedule the installer to be deleted on the next reboot. |
| if (!base::DeleteFileAfterReboot(gcp_setup_exe_path)) { |
| HRESULT last_error_hr = HRESULT_FROM_WIN32(::GetLastError()); |
| LOGFN(ERROR) << "DeleteFileAfterReboot hr=" << putHR(last_error_hr); |
| } |
| } |
| } else { |
| hr = credential_provider::DoInstall(gcp_setup_exe_path, |
| TEXT(CHROME_VERSION_STRING), nullptr); |
| } |
| |
| // Log success or failure only if uninstall was not launched as a separate |
| // process. |
| if (!(is_uninstall && path.empty())) { |
| if (::GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, nullptr, nullptr, |
| time_string, std::size(time_string)) == 0) { |
| HRESULT last_error_hr = HRESULT_FROM_WIN32(::GetLastError()); |
| LOGFN(ERROR) << "GetTimeFormatEx(end) hr=" << putHR(last_error_hr); |
| wcscpy_s(time_string, std::size(time_string), L"Unknown"); |
| } |
| |
| LOGFN(INFO) << (SUCCEEDED(hr) ? "Setup completed successfully" |
| : "Setup failed") |
| << ". " << time_string; |
| } |
| |
| return 0; |
| } |