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/logging.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/strings/stringize_macros.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "remoting/base/breakpad.h"
     21 #include "remoting/base/resources.h"
     22 #include "remoting/host/host_exit_codes.h"
     23 #include "remoting/host/logging.h"
     24 #include "remoting/host/setup/native_messaging_host.h"
     25 #include "remoting/host/usage_stats_consent.h"
     26 
     27 #if defined(OS_MACOSX)
     28 #include "base/mac/scoped_nsautorelease_pool.h"
     29 #endif  // defined(OS_MACOSX)
     30 
     31 #if defined(OS_WIN)
     32 #include <commctrl.h>
     33 #include <shellapi.h>
     34 #endif  // defined(OS_WIN)
     35 
     36 namespace remoting {
     37 
     38 // Known entry points.
     39 int DaemonProcessMain();
     40 int DesktopProcessMain();
     41 int ElevatedControllerMain();
     42 int HostProcessMain();
     43 int RdpDesktopSessionMain();
     44 
     45 const char kElevateSwitchName[] = "elevate";
     46 const char kProcessTypeSwitchName[] = "type";
     47 
     48 const char kProcessTypeController[] = "controller";
     49 const char kProcessTypeDaemon[] = "daemon";
     50 const char kProcessTypeDesktop[] = "desktop";
     51 const char kProcessTypeHost[] = "host";
     52 const char kProcessTypeNativeMessagingHost[] = "native_messaging_host";
     53 const char kProcessTypeRdpDesktopSession[] = "rdp_desktop_session";
     54 
     55 const char kExtensionOriginPrefix[] = "chrome-extension://";
     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 CommandLine::SwitchMap& switches =
     90       CommandLine::ForCurrentProcess()->GetSwitches();
     91   CommandLine::StringVector args = CommandLine::ForCurrentProcess()->GetArgs();
     92 
     93   // Create the child process command line by copying switches from the current
     94   // command line.
     95   CommandLine command_line(CommandLine::NO_PROGRAM);
     96   for (CommandLine::SwitchMap::const_iterator i = switches.begin();
     97        i != switches.end(); ++i) {
     98     if (i->first != kElevateSwitchName)
     99       command_line.AppendSwitchNative(i->first, i->second);
    100   }
    101   for (CommandLine::StringVector::const_iterator i = args.begin();
    102        i != args.end(); ++i) {
    103     command_line.AppendArgNative(*i);
    104   }
    105 
    106   // Get the name of the binary to launch.
    107   base::FilePath binary =
    108       CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName);
    109   CommandLine::StringType parameters = command_line.GetCommandLineString();
    110 
    111   // Launch the child process requesting elevation.
    112   SHELLEXECUTEINFO info;
    113   memset(&info, 0, sizeof(info));
    114   info.cbSize = sizeof(info);
    115   info.lpVerb = L"runas";
    116   info.lpFile = binary.value().c_str();
    117   info.lpParameters = parameters.c_str();
    118   info.nShow = SW_SHOWNORMAL;
    119 
    120   if (!ShellExecuteEx(&info)) {
    121     DWORD exit_code = GetLastError();
    122     LOG_GETLASTERROR(ERROR) << "Unable to launch '" << binary.value() << "'";
    123     return exit_code;
    124   }
    125 
    126   return kSuccessExitCode;
    127 }
    128 
    129 #else  // !defined(OS_WIN)
    130 
    131 // Fake entry points that exist only on Windows.
    132 int DaemonProcessMain() {
    133   NOTREACHED();
    134   return kInitializationFailed;
    135 }
    136 
    137 int DesktopProcessMain() {
    138   NOTREACHED();
    139   return kInitializationFailed;
    140 }
    141 
    142 int ElevatedControllerMain() {
    143   NOTREACHED();
    144   return kInitializationFailed;
    145 }
    146 
    147 int RdpDesktopSessionMain() {
    148   NOTREACHED();
    149   return kInitializationFailed;
    150 }
    151 
    152 #endif  // !defined(OS_WIN)
    153 
    154 // Select the entry point corresponding to the process type.
    155 MainRoutineFn SelectMainRoutine(const std::string& process_type) {
    156   MainRoutineFn main_routine = NULL;
    157 
    158   if (process_type == kProcessTypeDaemon) {
    159     main_routine = &DaemonProcessMain;
    160   } else if (process_type == kProcessTypeDesktop) {
    161     main_routine = &DesktopProcessMain;
    162   } else if (process_type == kProcessTypeController) {
    163     main_routine = &ElevatedControllerMain;
    164   } else if (process_type == kProcessTypeRdpDesktopSession) {
    165     main_routine = &RdpDesktopSessionMain;
    166   } else if (process_type == kProcessTypeHost) {
    167     main_routine = &HostProcessMain;
    168   } else if (process_type == kProcessTypeNativeMessagingHost) {
    169     main_routine = &NativeMessagingHostMain;
    170   }
    171 
    172   return main_routine;
    173 }
    174 
    175 }  // namespace
    176 
    177 int HostMain(int argc, char** argv) {
    178 #if defined(OS_MACOSX)
    179   // Needed so we don't leak objects when threads are created.
    180   base::mac::ScopedNSAutoreleasePool pool;
    181 #endif
    182 
    183   CommandLine::Init(argc, argv);
    184 
    185   // Initialize Breakpad as early as possible. On Mac the command-line needs to
    186   // be initialized first, so that the preference for crash-reporting can be
    187   // looked up in the config file.
    188 #if defined(REMOTING_ENABLE_BREAKPAD)
    189   if (IsUsageStatsAllowed()) {
    190     InitializeCrashReporting();
    191   }
    192 #endif  // defined(REMOTING_ENABLE_BREAKPAD)
    193 
    194   // This object instance is required by Chrome code (for example,
    195   // LazyInstance, MessageLoop).
    196   base::AtExitManager exit_manager;
    197 
    198   // Enable debug logs.
    199   InitHostLogging();
    200 
    201   // Register and initialize common controls.
    202 #if defined(OS_WIN)
    203   INITCOMMONCONTROLSEX info;
    204   info.dwSize = sizeof(info);
    205   info.dwICC = ICC_STANDARD_CLASSES;
    206   InitCommonControlsEx(&info);
    207 #endif  // defined(OS_WIN)
    208 
    209   // Parse the command line.
    210   const CommandLine* command_line = CommandLine::ForCurrentProcess();
    211   if (command_line->HasSwitch(kHelpSwitchName) ||
    212       command_line->HasSwitch(kQuestionSwitchName)) {
    213     Usage(command_line->GetProgram());
    214     return kSuccessExitCode;
    215   }
    216 
    217   if (command_line->HasSwitch(kVersionSwitchName)) {
    218     printf("%s\n", STRINGIZE(VERSION));
    219     return kSuccessExitCode;
    220   }
    221 
    222 #if defined(OS_WIN)
    223   if (command_line->HasSwitch(kElevateSwitchName)) {
    224     return RunElevated();
    225   }
    226 #endif  // defined(OS_WIN)
    227 
    228   // Assume the host process by default.
    229   std::string process_type = kProcessTypeHost;
    230   if (command_line->HasSwitch(kProcessTypeSwitchName)) {
    231     process_type = command_line->GetSwitchValueASCII(kProcessTypeSwitchName);
    232   } else {
    233     // Assume that it is the native messaging host starting if "--type" is
    234     // missing and the first argument looks like an origin that represents
    235     // an extension.
    236     CommandLine::StringVector args = command_line->GetArgs();
    237     if (!args.empty()) {
    238 #if defined(OS_WIN)
    239       std::string origin = UTF16ToUTF8(args[0]);
    240 #else
    241       std::string origin = args[0];
    242 #endif
    243       if (StartsWithASCII(origin, kExtensionOriginPrefix, true)) {
    244         process_type = kProcessTypeNativeMessagingHost;
    245       }
    246     }
    247   }
    248 
    249   MainRoutineFn main_routine = SelectMainRoutine(process_type);
    250   if (!main_routine) {
    251     fprintf(stderr, "Unknown process type '%s' specified.",
    252             process_type.c_str());
    253     Usage(command_line->GetProgram());
    254     return kUsageExitCode;
    255   }
    256 
    257   remoting::LoadResources("");
    258 
    259   // Invoke the entry point.
    260   int exit_code = main_routine();
    261   if (exit_code == kUsageExitCode) {
    262     Usage(command_line->GetProgram());
    263   }
    264 
    265   remoting::UnloadResources();
    266 
    267   return exit_code;
    268 }
    269 
    270 }  // namespace remoting
    271