Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // This file implements the common entry point shared by all Chromoting Host
      6 // processes.
      7 
      8 #include "remoting/host/host_main.h"
      9 
     10 #include <string>
     11 
     12 #include "base/at_exit.h"
     13 #include "base/command_line.h"
     14 #include "base/files/file_path.h"
     15 #include "base/i18n/icu_util.h"
     16 #include "base/logging.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/stringize_macros.h"
     19 #include "base/strings/stringprintf.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "remoting/base/breakpad.h"
     22 #include "remoting/base/resources.h"
     23 #include "remoting/host/host_exit_codes.h"
     24 #include "remoting/host/logging.h"
     25 #include "remoting/host/setup/me2me_native_messaging_host.h"
     26 #include "remoting/host/usage_stats_consent.h"
     27 
     28 #if defined(OS_MACOSX)
     29 #include "base/mac/scoped_nsautorelease_pool.h"
     30 #endif  // defined(OS_MACOSX)
     31 
     32 #if defined(OS_WIN)
     33 #include <commctrl.h>
     34 #include <shellapi.h>
     35 #endif  // defined(OS_WIN)
     36 
     37 namespace remoting {
     38 
     39 // Known entry points.
     40 int HostProcessMain();
     41 #if defined(OS_WIN)
     42 int DaemonProcessMain();
     43 int DesktopProcessMain();
     44 int ElevatedControllerMain();
     45 int RdpDesktopSessionMain();
     46 #endif  // defined(OS_WIN)
     47 
     48 const char kElevateSwitchName[] = "elevate";
     49 const char kProcessTypeSwitchName[] = "type";
     50 
     51 const char kProcessTypeController[] = "controller";
     52 const char kProcessTypeDaemon[] = "daemon";
     53 const char kProcessTypeDesktop[] = "desktop";
     54 const char kProcessTypeHost[] = "host";
     55 const char kProcessTypeRdpDesktopSession[] = "rdp_desktop_session";
     56 
     57 namespace {
     58 
     59 typedef int (*MainRoutineFn)();
     60 
     61 // "--help" or "--?" prints the usage message.
     62 const char kHelpSwitchName[] = "help";
     63 const char kQuestionSwitchName[] = "?";
     64 
     65 // The command line switch used to get version of the daemon.
     66 const char kVersionSwitchName[] = "version";
     67 
     68 const char kUsageMessage[] =
     69   "Usage: %s [options]\n"
     70   "\n"
     71   "Options:\n"
     72   "  --audio-pipe-name=<pipe> - Sets the pipe name to capture audio on Linux.\n"
     73   "  --console                - Runs the daemon interactively.\n"
     74   "  --daemon-pipe=<pipe>     - Specifies the pipe to connect to the daemon.\n"
     75   "  --elevate=<binary>       - Runs <binary> elevated.\n"
     76   "  --host-config=<config>   - Specifies the host configuration.\n"
     77   "  --help, -?               - Print this message.\n"
     78   "  --type                   - Specifies process type.\n"
     79   "  --version                - Prints the host version and exits.\n";
     80 
     81 void Usage(const base::FilePath& program_name) {
     82   printf(kUsageMessage, program_name.MaybeAsASCII().c_str());
     83 }
     84 
     85 #if defined(OS_WIN)
     86 
     87 // Runs the binary specified by the command line, elevated.
     88 int RunElevated() {
     89   const base::CommandLine::SwitchMap& switches =
     90       base::CommandLine::ForCurrentProcess()->GetSwitches();
     91   base::CommandLine::StringVector args =
     92       base::CommandLine::ForCurrentProcess()->GetArgs();
     93 
     94   // Create the child process command line by copying switches from the current
     95   // command line.
     96   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     97   for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
     98        i != switches.end(); ++i) {
     99     if (i->first != kElevateSwitchName)
    100       command_line.AppendSwitchNative(i->first, i->second);
    101   }
    102   for (base::CommandLine::StringVector::const_iterator i = args.begin();
    103        i != args.end(); ++i) {
    104     command_line.AppendArgNative(*i);
    105   }
    106 
    107   // Get the name of the binary to launch.
    108   base::FilePath binary =
    109       base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
    110           kElevateSwitchName);
    111   base::CommandLine::StringType parameters =
    112       command_line.GetCommandLineString();
    113 
    114   // Launch the child process requesting elevation.
    115   SHELLEXECUTEINFO info;
    116   memset(&info, 0, sizeof(info));
    117   info.cbSize = sizeof(info);
    118   info.lpVerb = L"runas";
    119   info.lpFile = binary.value().c_str();
    120   info.lpParameters = parameters.c_str();
    121   info.nShow = SW_SHOWNORMAL;
    122 
    123   if (!ShellExecuteEx(&info)) {
    124     DWORD exit_code = GetLastError();
    125     PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
    126     return exit_code;
    127   }
    128 
    129   return kSuccessExitCode;
    130 }
    131 
    132 #endif  // !defined(OS_WIN)
    133 
    134 // Select the entry point corresponding to the process type.
    135 MainRoutineFn SelectMainRoutine(const std::string& process_type) {
    136   MainRoutineFn main_routine = NULL;
    137 
    138   if (process_type == kProcessTypeHost) {
    139     main_routine = &HostProcessMain;
    140 #if defined(OS_WIN)
    141   } else if (process_type == kProcessTypeDaemon) {
    142     main_routine = &DaemonProcessMain;
    143   } else if (process_type == kProcessTypeDesktop) {
    144     main_routine = &DesktopProcessMain;
    145   } else if (process_type == kProcessTypeController) {
    146     main_routine = &ElevatedControllerMain;
    147   } else if (process_type == kProcessTypeRdpDesktopSession) {
    148     main_routine = &RdpDesktopSessionMain;
    149 #endif  // defined(OS_WIN)
    150   }
    151 
    152   return main_routine;
    153 }
    154 
    155 }  // namespace
    156 
    157 int HostMain(int argc, char** argv) {
    158 #if defined(OS_MACOSX)
    159   // Needed so we don't leak objects when threads are created.
    160   base::mac::ScopedNSAutoreleasePool pool;
    161 #endif
    162 
    163   base::CommandLine::Init(argc, argv);
    164 
    165   // Initialize Breakpad as early as possible. On Mac the command-line needs to
    166   // be initialized first, so that the preference for crash-reporting can be
    167   // looked up in the config file.
    168 #if defined(REMOTING_ENABLE_BREAKPAD)
    169   if (IsUsageStatsAllowed()) {
    170     InitializeCrashReporting();
    171   }
    172 #endif  // defined(REMOTING_ENABLE_BREAKPAD)
    173 
    174   // This object instance is required by Chrome code (for example,
    175   // LazyInstance, MessageLoop).
    176   base::AtExitManager exit_manager;
    177 
    178   // Enable debug logs.
    179   InitHostLogging();
    180 
    181   // Register and initialize common controls.
    182 #if defined(OS_WIN)
    183   INITCOMMONCONTROLSEX info;
    184   info.dwSize = sizeof(info);
    185   info.dwICC = ICC_STANDARD_CLASSES;
    186   InitCommonControlsEx(&info);
    187 #endif  // defined(OS_WIN)
    188 
    189   // Parse the command line.
    190   const base::CommandLine* command_line =
    191       base::CommandLine::ForCurrentProcess();
    192   if (command_line->HasSwitch(kHelpSwitchName) ||
    193       command_line->HasSwitch(kQuestionSwitchName)) {
    194     Usage(command_line->GetProgram());
    195     return kSuccessExitCode;
    196   }
    197 
    198   if (command_line->HasSwitch(kVersionSwitchName)) {
    199     printf("%s\n", STRINGIZE(VERSION));
    200     return kSuccessExitCode;
    201   }
    202 
    203 #if defined(OS_WIN)
    204   if (command_line->HasSwitch(kElevateSwitchName)) {
    205     return RunElevated();
    206   }
    207 #endif  // defined(OS_WIN)
    208 
    209   // Assume the host process by default.
    210   std::string process_type = kProcessTypeHost;
    211   if (command_line->HasSwitch(kProcessTypeSwitchName)) {
    212     process_type = command_line->GetSwitchValueASCII(kProcessTypeSwitchName);
    213   }
    214 
    215   MainRoutineFn main_routine = SelectMainRoutine(process_type);
    216   if (!main_routine) {
    217     fprintf(stderr, "Unknown process type '%s' specified.",
    218             process_type.c_str());
    219     Usage(command_line->GetProgram());
    220     return kUsageExitCode;
    221   }
    222 
    223   // Required to find the ICU data file, used by some file_util routines.
    224   base::i18n::InitializeICU();
    225 
    226   remoting::LoadResources("");
    227 
    228   // Invoke the entry point.
    229   int exit_code = main_routine();
    230   if (exit_code == kUsageExitCode) {
    231     Usage(command_line->GetProgram());
    232   }
    233 
    234   remoting::UnloadResources();
    235 
    236   return exit_code;
    237 }
    238 
    239 }  // namespace remoting
    240