blob: 5e49a7b373a49b2eb349380ccc0479133568d173 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/updater.h"
#include <algorithm>
#include <iterator>
#include "base/at_exit.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/message_loop/message_pump_type.h"
#include "base/process/memory.h"
#include "base/task/single_thread_task_executor.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "chrome/updater/app/app.h"
#include "chrome/updater/app/app_install.h"
#include "chrome/updater/app/app_recover.h"
#include "chrome/updater/app/app_uninstall.h"
#include "chrome/updater/app/app_update.h"
#include "chrome/updater/app/app_wake.h"
#include "chrome/updater/configurator.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/crash_client.h"
#include "chrome/updater/crash_reporter.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util.h"
#include "components/crash/core/common/crash_key.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/process_startup_helper.h"
#include "base/win/scoped_com_initializer.h"
#include "chrome/updater/app/server/win/server.h"
#include "chrome/updater/app/server/win/service_main.h"
#include "chrome/updater/win/win_util.h"
#elif BUILDFLAG(IS_MAC)
#include "chrome/updater/app/server/mac/server.h"
#elif BUILDFLAG(IS_LINUX)
#include "chrome/updater/app/server/linux/server.h"
#endif
// Instructions For Windows.
// - To install only the updater, run "updatersetup.exe" from the build out dir.
// - To install Chrome and the updater, do the same but use the --app-id:
// updatersetup.exe --install --app-id={8A69D345-D564-463c-AFF1-A69D9E530F96}
// - To uninstall, run "updater.exe --uninstall" from its install directory,
// which is under %LOCALAPPDATA%\Google\GoogleUpdater, or from the |out|
// directory of the build.
// - To debug, append the following arguments to any updater command line:
// --enable-logging --vmodule=*/chrome/updater/*=2,*/components/winhttp/*=2.
// - To run the `updater --install` from the `out` directory of the build,
// use --install-from-out-dir command line switch in addition to other
// arguments for --install.
namespace updater {
namespace {
void ReinitializeLoggingAfterCrashHandler(UpdaterScope updater_scope) {
// Initializing the logging more than two times is not supported. In this
// case, logging has been initialized once in the updater main, and the
// the second time by the crash handler.
// Reinitializing the log is not possible if the vlog switch is
// already present on the command line. The code in this function relies
// on undocumented behavior of the logging object, and it could break.
base::CommandLine::ForCurrentProcess()->RemoveSwitch(kLoggingModuleSwitch);
InitLogging(updater_scope);
}
void InitializeCrashReporting(UpdaterScope updater_scope) {
crash_reporter::InitializeCrashKeys();
static crash_reporter::CrashKeyString<16> crash_key_process_type(
"process_type");
crash_key_process_type.Set("updater");
if (!CrashClient::GetInstance()->InitializeCrashReporting(updater_scope)) {
VLOG(1) << "Crash reporting is not available.";
return;
}
VLOG(1) << "Crash reporting initialized.";
}
int HandleUpdaterCommands(UpdaterScope updater_scope,
const base::CommandLine* command_line) {
// Used for unit test purposes. There is no need to run with a crash handler.
if (command_line->HasSwitch(kTestSwitch))
return kErrorOk;
if (command_line->HasSwitch(kCrashHandlerSwitch)) {
const int retval = CrashReporterMain();
// The crash handler mutates the logging object, so the updater process
// stops logging to the log file aftern `CrashReporterMain()` returns.
ReinitializeLoggingAfterCrashHandler(updater_scope);
return retval;
}
// Starts and connects to the external crash handler as early as possible.
StartCrashReporter(updater_scope, kUpdaterVersion);
InitializeCrashReporting(updater_scope);
// Make the process more resilient to memory allocation issues.
base::EnableTerminationOnHeapCorruption();
base::EnableTerminationOnOutOfMemory();
#if BUILDFLAG(IS_WIN)
base::win::ScopedCOMInitializer com_initializer(
base::win::ScopedCOMInitializer::kMTA);
if (!com_initializer.Succeeded()) {
PLOG(ERROR) << "Failed to initialize COM";
// TODO(crbug.com/1294543) - is there a more specific error needed?
return kErrorComInitializationFailed;
}
if (FAILED(DisableCOMExceptionHandling())) {
// Failing to disable COM exception handling is a critical error.
CHECK(false) << "Failed to disable COM exception handling.";
}
base::win::RegisterInvalidParamHandler();
VLOG(1) << GetUACState();
#endif
base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
if (command_line->HasSwitch(kCrashMeSwitch)) {
// Records a backtrace in the log, crashes the program, saves a crash dump,
// and reports the crash.
CHECK(false) << "--crash-me was used.";
}
if (command_line->HasSwitch(kServerSwitch)) {
#if BUILDFLAG(IS_WIN)
// By design, Windows uses a leaky singleton server for its RPC server.
return AppServerSingletonInstance()->Run();
#else
return MakeAppServer()->Run();
#endif
}
if (command_line->HasSwitch(kUpdateSwitch))
return MakeAppUpdate()->Run();
#if BUILDFLAG(IS_WIN)
if (command_line->HasSwitch(kWindowsServiceSwitch))
return ServiceMain::RunWindowsService(command_line);
if (command_line->HasSwitch(kHealthCheckSwitch)) {
return kErrorOk;
}
#endif // BUILDFLAG(IS_WIN)
if (command_line->HasSwitch(kInstallSwitch) ||
command_line->HasSwitch(kTagSwitch) ||
command_line->HasSwitch(kHandoffSwitch)) {
return MakeAppInstall()->Run();
}
if (command_line->HasSwitch(kUninstallSwitch) ||
command_line->HasSwitch(kUninstallSelfSwitch) ||
command_line->HasSwitch(kUninstallIfUnusedSwitch)) {
return MakeAppUninstall()->Run();
}
if (command_line->HasSwitch(kRecoverSwitch) ||
command_line->HasSwitch(kBrowserVersionSwitch)) {
return MakeAppRecover()->Run();
}
if (command_line->HasSwitch(kWakeSwitch)) {
return MakeAppWake()->Run();
}
VLOG(1) << "Unknown command line switch.";
return kErrorUnknownCommandLine;
}
// Returns the string literal corresponding to an updater command, which
// is present on the updater process command line. Returns an empty string
// if the command is not found.
const char* GetUpdaterCommand(const base::CommandLine* command_line) {
// Contains the literals which are associated with specific updater commands.
const char* commands[] = {
kWindowsServiceSwitch,
kCrashHandlerSwitch,
kInstallSwitch,
kRecoverSwitch,
kServerSwitch,
kTagSwitch,
kTestSwitch,
kUninstallIfUnusedSwitch,
kUninstallSelfSwitch,
kUninstallSwitch,
kUpdateSwitch,
kWakeSwitch,
kHealthCheckSwitch,
kHandoffSwitch,
};
const char** it = std::find_if(
std::begin(commands), std::end(commands),
[command_line](auto cmd) { return command_line->HasSwitch(cmd); });
// Return the command. As a workaround for recovery component invocations
// that do not pass --recover, report the browser version switch as --recover.
return it != std::end(commands)
? *it
: command_line->HasSwitch(kBrowserVersionSwitch) ? kRecoverSwitch
: "";
}
constexpr const char* BuildFlavor() {
#if defined(NBEDUG)
return "opt";
#else
return "debug";
#endif
}
constexpr const char* BuildArch() {
#if defined(ARCH_CPU_64_BITS)
return "64 bits";
#elif defined(ARCH_CPU_32_BITS)
return "32 bits";
#else
#error CPU architecture is unknown.
#endif
}
} // namespace
int UpdaterMain(int argc, const char* const* argv) {
base::PlatformThread::SetName("UpdaterMain");
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
const UpdaterScope updater_scope = GetUpdaterScope();
InitLogging(updater_scope);
VLOG(1) << "Version " << kUpdaterVersion << ", " << BuildFlavor() << ", "
<< BuildArch()
<< ", command line: " << command_line->GetCommandLineString();
const int retval = HandleUpdaterCommands(updater_scope, command_line);
VLOG(1) << __func__ << " (--" << GetUpdaterCommand(command_line) << ")"
<< " returned " << retval << ".";
return retval;
}
} // namespace updater