blob: b3473d5e1bf73fd6e4c633bd22eec1616b044580 [file]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements the common entry point shared by all Chromoting Host
// processes.
#include "remoting/host/host_main.h"
#include <string>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/i18n/icu_util.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/stringize_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "mojo/core/embedder/embedder.h"
#include "remoting/base/crash/crash_reporting_crashpad.h"
#include "remoting/base/logging.h"
#if BUILDFLAG(IS_LINUX)
#include <sys/stat.h>
#include <unistd.h>
#include "base/files/file_util.h"
#include "base/posix/eintr_wrapper.h"
#include "remoting/base/file_path_util_linux.h"
#endif // BUILDFLAG(IS_LINUX)
#include "remoting/host/base/host_exit_codes.h"
#include "remoting/host/base/switches.h"
#include "remoting/host/evaluate_capability.h"
#include "remoting/host/resources.h"
#include "remoting/host/setup/me2me_native_messaging_host.h"
#include "remoting/host/usage_stats_consent.h"
#if BUILDFLAG(IS_APPLE)
#include "base/apple/scoped_nsautorelease_pool.h"
#endif // BUILDFLAG(IS_APPLE)
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include <commctrl.h>
#include <shellapi.h>
#include "remoting/base/crash/crash_reporting_breakpad.h"
#endif // BUILDFLAG(IS_WIN)
namespace remoting {
// Known entry points.
int SingleProcessHostProcessMain();
int NetworkProcessMain();
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
int DaemonProcessMain();
int DesktopProcessMain();
#endif
#if BUILDFLAG(IS_WIN)
int FileChooserMain();
int RdpDesktopSessionMain();
int UrlForwarderConfiguratorMain();
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
int XSessionChooserMain();
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
namespace {
#if BUILDFLAG(IS_LINUX)
void EnsureVarLibDirectory() {
if (getuid() != 0) {
// Only do this in the daemon process, which is always run as root.
return;
}
base::FilePath var_lib_dir = GetVarLibDir();
if (base::PathExists(var_lib_dir) && !base::DirectoryExists(var_lib_dir)) {
if (!base::DeletePathRecursively(var_lib_dir)) {
PLOG(FATAL) << "Failed to delete non-directory " << var_lib_dir;
}
}
base::File::Error error;
if (!base::CreateDirectoryAndGetError(var_lib_dir, &error)) {
LOG(FATAL) << "Failed to create " << var_lib_dir << ": "
<< base::File::ErrorToString(error);
}
// Allow other users to list and read files and directories, but not write to
// it.
if (HANDLE_EINTR(chmod(var_lib_dir.value().c_str(), 0755)) != 0) {
PLOG(ERROR) << "Failed to chmod " << var_lib_dir;
if (!base::DeletePathRecursively(var_lib_dir)) {
PLOG(FATAL) << "Failed to delete " << var_lib_dir;
}
}
}
#endif // BUILDFLAG(IS_LINUX)
typedef int (*MainRoutineFn)();
void Usage(const base::FilePath& program_name) {
printf(
"Usage: %s [options]\n"
"\n"
"Options:\n"
#if BUILDFLAG(IS_LINUX)
" --audio-pipe-name=<pipe> - Sets the pipe name to capture audio on "
"Linux.\n"
#endif // BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_APPLE)
" --list-audio-devices - List all audio devices and their device "
"UID.\n"
#endif // BUILDFLAG(IS_APPLE)
" --console - Runs the daemon interactively.\n"
" --elevate=<binary> - Runs <binary> elevated.\n"
" --host-config=<config> - Specifies the host configuration.\n"
" --help, -? - Prints this message.\n"
" --type - Specifies process type.\n"
" --version - Prints the host version and exits.\n"
" --evaluate-type=<type> - Evaluates the capability of the host.\n"
" --enable-wtmpdb - Enables recording to wtmpdb on Linux.\n"
" --webrtc-trace-event-file=<path> - Enables logging webrtc trace events"
" to a file.\n",
program_name.MaybeAsASCII().c_str());
}
#if BUILDFLAG(IS_WIN)
// Runs the binary specified by the command line, elevated.
int RunElevated() {
const base::CommandLine::SwitchMap& switches =
base::CommandLine::ForCurrentProcess()->GetSwitches();
base::CommandLine::StringVector args =
base::CommandLine::ForCurrentProcess()->GetArgs();
// Create the child process command line by copying switches from the current
// command line.
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
i != switches.end(); ++i) {
if (i->first != kElevateSwitchName) {
command_line.AppendSwitchNative(i->first, i->second);
}
}
for (base::CommandLine::StringVector::const_iterator i = args.begin();
i != args.end(); ++i) {
command_line.AppendArgNative(*i);
}
// Get the name of the binary to launch.
base::FilePath binary =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
kElevateSwitchName);
base::CommandLine::StringType parameters =
command_line.GetCommandLineString();
// Launch the child process requesting elevation.
SHELLEXECUTEINFO info = {};
info.cbSize = sizeof(info);
info.lpVerb = L"runas";
info.lpFile = binary.value().c_str();
info.lpParameters = parameters.c_str();
info.nShow = SW_SHOWNORMAL;
if (!ShellExecuteEx(&info)) {
DWORD exit_code = GetLastError();
PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
return exit_code;
}
return kSuccessExitCode;
}
#endif // !BUILDFLAG(IS_WIN)
// Select the entry point corresponding to the process type.
MainRoutineFn SelectMainRoutine(const std::string& process_type) {
MainRoutineFn main_routine = nullptr;
if (process_type == kProcessTypeSingleProcessHost) {
main_routine = &SingleProcessHostProcessMain;
} else if (process_type == kProcessTypeNetwork) {
main_routine = &NetworkProcessMain;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
} else if (process_type == kProcessTypeDaemon) {
main_routine = &DaemonProcessMain;
} else if (process_type == kProcessTypeDesktop) {
main_routine = &DesktopProcessMain;
#endif
#if BUILDFLAG(IS_WIN)
} else if (process_type == kProcessTypeFileChooser) {
main_routine = &FileChooserMain;
} else if (process_type == kProcessTypeRdpDesktopSession) {
main_routine = &RdpDesktopSessionMain;
} else if (process_type == kProcessTypeUrlForwarderConfigurator) {
main_routine = &UrlForwarderConfiguratorMain;
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
} else if (process_type == kProcessTypeXSessionChooser) {
main_routine = &XSessionChooserMain;
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
}
return main_routine;
}
} // namespace
int HostMain(int argc, char** argv) {
#if BUILDFLAG(IS_APPLE)
// Needed so we don't leak objects when threads are created.
base::apple::ScopedNSAutoreleasePool pool;
#endif
base::CommandLine::Init(argc, argv);
// Parse the command line.
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kHelpSwitchName) ||
command_line->HasSwitch(kQuestionSwitchName)) {
Usage(command_line->GetProgram());
return kSuccessExitCode;
}
if (command_line->HasSwitch(kVersionSwitchName)) {
printf("%s\n", STRINGIZE(VERSION));
return kSuccessExitCode;
}
#if BUILDFLAG(IS_WIN)
if (command_line->HasSwitch(kElevateSwitchName)) {
return RunElevated();
}
#endif // BUILDFLAG(IS_WIN)
// Assume the single-process host process by default.
std::string process_type = kProcessTypeSingleProcessHost;
if (command_line->HasSwitch(kProcessTypeSwitchName)) {
process_type = command_line->GetSwitchValueASCII(kProcessTypeSwitchName);
}
if (process_type == kProcessTypeEvaluateCapability) {
if (command_line->HasSwitch(kEvaluateCapabilitySwitchName)) {
return EvaluateCapabilityLocally(
command_line->GetSwitchValueASCII(kEvaluateCapabilitySwitchName));
}
Usage(command_line->GetProgram());
return kSuccessExitCode;
}
// This object instance is required by Chrome code (for example,
// LazyInstance, MessageLoop).
base::AtExitManager exit_manager;
// Enable debug logs.
InitHostLogging();
#if BUILDFLAG(IS_LINUX)
EnsureVarLibDirectory();
#endif // BUILDFLAG(IS_LINUX)
#if defined(REMOTING_ENABLE_CRASH_REPORTING)
// Initialize crash reporting as early as possible. On Mac the command-line
// needs to be initialized first, so that the preference for crash-reporting
// can be looked up in the config file.
// Note that we enable crash reporting only if the user has opted in to having
// the crash reports uploaded.
if (IsUsageStatsAllowed()) {
#if BUILDFLAG(IS_LINUX)
InitializeCrashpadReporting();
#elif BUILDFLAG(IS_WIN)
// TODO: joedow - Enable crash reporting for the RDP process.
if (process_type == kProcessTypeDaemon) {
InitializeBreakpadReporting();
} else if (process_type == kProcessTypeDesktop) {
// TODO(garykac): Switch to use InitializeCrashpadReporting();
InitializeBreakpadReporting();
} else if (command_line->HasSwitch(kCrashServerPipeHandle)) {
InitializeOopCrashClient(
command_line->GetSwitchValueASCII(kCrashServerPipeHandle));
}
#endif
}
#endif // defined(REMOTING_ENABLE_CRASH_REPORTING)
#if BUILDFLAG(IS_WIN)
// Register and initialize common controls.
INITCOMMONCONTROLSEX info;
info.dwSize = sizeof(info);
info.dwICC = ICC_STANDARD_CLASSES;
InitCommonControlsEx(&info);
#endif // BUILDFLAG(IS_WIN)
MainRoutineFn main_routine = SelectMainRoutine(process_type);
if (!main_routine) {
fprintf(stderr, "Unknown process type '%s' specified.",
process_type.c_str());
Usage(command_line->GetProgram());
return kInvalidCommandLineExitCode;
}
// Required to find the ICU data file, used by some file_util routines.
base::i18n::InitializeICU();
remoting::LoadResources("");
bool is_broker_process = false;
#if !BUILDFLAG(IS_MAC)
// The single-process host process should act as the mojo broker, except on
// Mac, where the broker process is the agent process broker.
is_broker_process |= main_routine == &SingleProcessHostProcessMain;
#endif
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
// For multi-process hosts, the daemon process acts as the broker.
is_broker_process |= main_routine == &DaemonProcessMain;
#endif
mojo::core::Init({.is_broker_process = is_broker_process});
// Invoke the entry point.
int exit_code = main_routine();
if (exit_code == kInvalidCommandLineExitCode) {
Usage(command_line->GetProgram());
}
remoting::UnloadResources();
return exit_code;
}
} // namespace remoting