// Copyright (c) 2013 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. // // 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/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/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "remoting/base/breakpad.h" #include "remoting/base/resources.h" #include "remoting/host/host_exit_codes.h" #include "remoting/host/logging.h" #include "remoting/host/setup/me2me_native_messaging_host.h" #include "remoting/host/usage_stats_consent.h" #if defined(OS_MACOSX) #include "base/mac/scoped_nsautorelease_pool.h" #endif // defined(OS_MACOSX) #if defined(OS_WIN) #include <commctrl.h> #include <shellapi.h> #endif // defined(OS_WIN) namespace remoting { // Known entry points. int HostProcessMain(); #if defined(OS_WIN) int DaemonProcessMain(); int DesktopProcessMain(); int ElevatedControllerMain(); int RdpDesktopSessionMain(); #endif // defined(OS_WIN) const char kElevateSwitchName[] = "elevate"; const char kProcessTypeSwitchName[] = "type"; const char kProcessTypeController[] = "controller"; const char kProcessTypeDaemon[] = "daemon"; const char kProcessTypeDesktop[] = "desktop"; const char kProcessTypeHost[] = "host"; const char kProcessTypeRdpDesktopSession[] = "rdp_desktop_session"; namespace { typedef int (*MainRoutineFn)(); // "--help" or "--?" prints the usage message. const char kHelpSwitchName[] = "help"; const char kQuestionSwitchName[] = "?"; // The command line switch used to get version of the daemon. const char kVersionSwitchName[] = "version"; const char kUsageMessage[] = "Usage: %s [options]\n" "\n" "Options:\n" " --audio-pipe-name=<pipe> - Sets the pipe name to capture audio on Linux.\n" " --console - Runs the daemon interactively.\n" " --daemon-pipe=<pipe> - Specifies the pipe to connect to the daemon.\n" " --elevate=<binary> - Runs <binary> elevated.\n" " --host-config=<config> - Specifies the host configuration.\n" " --help, -? - Print this message.\n" " --type - Specifies process type.\n" " --version - Prints the host version and exits.\n" " --window-id=<id> - Specifies a window to remote," " instead of the whole desktop.\n"; void Usage(const base::FilePath& program_name) { printf(kUsageMessage, program_name.MaybeAsASCII().c_str()); } #if defined(OS_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; memset(&info, 0, sizeof(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 // !defined(OS_WIN) // Select the entry point corresponding to the process type. MainRoutineFn SelectMainRoutine(const std::string& process_type) { MainRoutineFn main_routine = NULL; if (process_type == kProcessTypeHost) { main_routine = &HostProcessMain; #if defined(OS_WIN) } else if (process_type == kProcessTypeDaemon) { main_routine = &DaemonProcessMain; } else if (process_type == kProcessTypeDesktop) { main_routine = &DesktopProcessMain; } else if (process_type == kProcessTypeController) { main_routine = &ElevatedControllerMain; } else if (process_type == kProcessTypeRdpDesktopSession) { main_routine = &RdpDesktopSessionMain; #endif // defined(OS_WIN) } return main_routine; } } // namespace int HostMain(int argc, char** argv) { #if defined(OS_MACOSX) // Needed so we don't leak objects when threads are created. base::mac::ScopedNSAutoreleasePool pool; #endif base::CommandLine::Init(argc, argv); // Initialize Breakpad 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. #if defined(REMOTING_ENABLE_BREAKPAD) if (IsUsageStatsAllowed()) { InitializeCrashReporting(); } #endif // defined(REMOTING_ENABLE_BREAKPAD) // This object instance is required by Chrome code (for example, // LazyInstance, MessageLoop). base::AtExitManager exit_manager; // Enable debug logs. InitHostLogging(); // Register and initialize common controls. #if defined(OS_WIN) INITCOMMONCONTROLSEX info; info.dwSize = sizeof(info); info.dwICC = ICC_STANDARD_CLASSES; InitCommonControlsEx(&info); #endif // defined(OS_WIN) // 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 defined(OS_WIN) if (command_line->HasSwitch(kElevateSwitchName)) { return RunElevated(); } #endif // defined(OS_WIN) // Assume the host process by default. std::string process_type = kProcessTypeHost; if (command_line->HasSwitch(kProcessTypeSwitchName)) { process_type = command_line->GetSwitchValueASCII(kProcessTypeSwitchName); } 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 kUsageExitCode; } // Required to find the ICU data file, used by some file_util routines. base::i18n::InitializeICU(); remoting::LoadResources(""); // Invoke the entry point. int exit_code = main_routine(); if (exit_code == kUsageExitCode) { Usage(command_line->GetProgram()); } remoting::UnloadResources(); return exit_code; } } // namespace remoting #if !defined(OS_WIN) int main(int argc, char** argv) { return remoting::HostMain(argc, argv); } #endif // !defined(OS_WIN)