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/me2me_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 HostProcessMain();
     40 #if defined(OS_WIN)
     41 int DaemonProcessMain();
     42 int DesktopProcessMain();
     43 int ElevatedControllerMain();
     44 int RdpDesktopSessionMain();
     45 #endif  // defined(OS_WIN)
     46 
     47 const char kElevateSwitchName[] = "elevate";
     48 const char kProcessTypeSwitchName[] = "type";
     49 
     50 const char kProcessTypeController[] = "controller";
     51 const char kProcessTypeDaemon[] = "daemon";
     52 const char kProcessTypeDesktop[] = "desktop";
     53 const char kProcessTypeHost[] = "host";
     54 const char kProcessTypeNativeMessagingHost[] = "native_messaging_host";
     55 const char kProcessTypeRdpDesktopSession[] = "rdp_desktop_session";
     56 
     57 const char kExtensionOriginPrefix[] = "chrome-extension://";
     58 
     59 namespace {
     60 
     61 typedef int (*MainRoutineFn)();
     62 
     63 // "--help" or "--?" prints the usage message.
     64 const char kHelpSwitchName[] = "help";
     65 const char kQuestionSwitchName[] = "?";
     66 
     67 // The command line switch used to get version of the daemon.
     68 const char kVersionSwitchName[] = "version";
     69 
     70 const char kUsageMessage[] =
     71   "Usage: %s [options]\n"
     72   "\n"
     73   "Options:\n"
     74   "  --audio-pipe-name=<pipe> - Sets the pipe name to capture audio on Linux.\n"
     75   "  --console                - Runs the daemon interactively.\n"
     76   "  --daemon-pipe=<pipe>     - Specifies the pipe to connect to the daemon.\n"
     77   "  --elevate=<binary>       - Runs <binary> elevated.\n"
     78   "  --host-config=<config>   - Specifies the host configuration.\n"
     79   "  --help, -?               - Print this message.\n"
     80   "  --type                   - Specifies process type.\n"
     81   "  --version                - Prints the host version and exits.\n";
     82 
     83 void Usage(const base::FilePath& program_name) {
     84   printf(kUsageMessage, program_name.MaybeAsASCII().c_str());
     85 }
     86 
     87 #if defined(OS_WIN)
     88 
     89 // Runs the binary specified by the command line, elevated.
     90 int RunElevated() {
     91   const CommandLine::SwitchMap& switches =
     92       CommandLine::ForCurrentProcess()->GetSwitches();
     93   CommandLine::StringVector args = CommandLine::ForCurrentProcess()->GetArgs();
     94 
     95   // Create the child process command line by copying switches from the current
     96   // command line.
     97   CommandLine command_line(CommandLine::NO_PROGRAM);
     98   for (CommandLine::SwitchMap::const_iterator i = switches.begin();
     99        i != switches.end(); ++i) {
    100     if (i->first != kElevateSwitchName)
    101       command_line.AppendSwitchNative(i->first, i->second);
    102   }
    103   for (CommandLine::StringVector::const_iterator i = args.begin();
    104        i != args.end(); ++i) {
    105     command_line.AppendArgNative(*i);
    106   }
    107 
    108   // Get the name of the binary to launch.
    109   base::FilePath binary =
    110       CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName);
    111   CommandLine::StringType parameters = command_line.GetCommandLineString();
    112 
    113   // Launch the child process requesting elevation.
    114   SHELLEXECUTEINFO info;
    115   memset(&info, 0, sizeof(info));
    116   info.cbSize = sizeof(info);
    117   info.lpVerb = L"runas";
    118   info.lpFile = binary.value().c_str();
    119   info.lpParameters = parameters.c_str();
    120   info.nShow = SW_SHOWNORMAL;
    121 
    122   if (!ShellExecuteEx(&info)) {
    123     DWORD exit_code = GetLastError();
    124     LOG_GETLASTERROR(ERROR) << "Unable to launch '" << binary.value() << "'";
    125     return exit_code;
    126   }
    127 
    128   return kSuccessExitCode;
    129 }
    130 
    131 #endif  // !defined(OS_WIN)
    132 
    133 // Select the entry point corresponding to the process type.
    134 MainRoutineFn SelectMainRoutine(const std::string& process_type) {
    135   MainRoutineFn main_routine = NULL;
    136 
    137   if (process_type == kProcessTypeHost) {
    138     main_routine = &HostProcessMain;
    139 #if defined(OS_WIN)
    140   } else if (process_type == kProcessTypeDaemon) {
    141     main_routine = &DaemonProcessMain;
    142   } else if (process_type == kProcessTypeDesktop) {
    143     main_routine = &DesktopProcessMain;
    144   } else if (process_type == kProcessTypeController) {
    145     main_routine = &ElevatedControllerMain;
    146   } else if (process_type == kProcessTypeRdpDesktopSession) {
    147     main_routine = &RdpDesktopSessionMain;
    148   } else if (process_type == kProcessTypeNativeMessagingHost) {
    149     main_routine = &Me2MeNativeMessagingHostMain;
    150 #endif  // defined(OS_WIN)
    151   }
    152 
    153   return main_routine;
    154 }
    155 
    156 }  // namespace
    157 
    158 int HostMain(int argc, char** argv) {
    159 #if defined(OS_MACOSX)
    160   // Needed so we don't leak objects when threads are created.
    161   base::mac::ScopedNSAutoreleasePool pool;
    162 #endif
    163 
    164   CommandLine::Init(argc, argv);
    165 
    166   // Initialize Breakpad as early as possible. On Mac the command-line needs to
    167   // be initialized first, so that the preference for crash-reporting can be
    168   // looked up in the config file.
    169 #if defined(REMOTING_ENABLE_BREAKPAD)
    170   if (IsUsageStatsAllowed()) {
    171     InitializeCrashReporting();
    172   }
    173 #endif  // defined(REMOTING_ENABLE_BREAKPAD)
    174 
    175   // This object instance is required by Chrome code (for example,
    176   // LazyInstance, MessageLoop).
    177   base::AtExitManager exit_manager;
    178 
    179   // Enable debug logs.
    180   InitHostLogging();
    181 
    182   // Register and initialize common controls.
    183 #if defined(OS_WIN)
    184   INITCOMMONCONTROLSEX info;
    185   info.dwSize = sizeof(info);
    186   info.dwICC = ICC_STANDARD_CLASSES;
    187   InitCommonControlsEx(&info);
    188 #endif  // defined(OS_WIN)
    189 
    190   // Parse the command line.
    191   const CommandLine* command_line = 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   } else {
    214     // Assume that it is the native messaging host starting if "--type" is
    215     // missing and the first argument looks like an origin that represents
    216     // an extension.
    217     CommandLine::StringVector args = command_line->GetArgs();
    218     if (!args.empty()) {
    219 #if defined(OS_WIN)
    220       std::string origin = UTF16ToUTF8(args[0]);
    221 #else
    222       std::string origin = args[0];
    223 #endif
    224       if (StartsWithASCII(origin, kExtensionOriginPrefix, true)) {
    225         process_type = kProcessTypeNativeMessagingHost;
    226       }
    227     }
    228   }
    229 
    230   MainRoutineFn main_routine = SelectMainRoutine(process_type);
    231   if (!main_routine) {
    232     fprintf(stderr, "Unknown process type '%s' specified.",
    233             process_type.c_str());
    234     Usage(command_line->GetProgram());
    235     return kUsageExitCode;
    236   }
    237 
    238   remoting::LoadResources("");
    239 
    240   // Invoke the entry point.
    241   int exit_code = main_routine();
    242   if (exit_code == kUsageExitCode) {
    243     Usage(command_line->GetProgram());
    244   }
    245 
    246   remoting::UnloadResources();
    247 
    248   return exit_code;
    249 }
    250 
    251 }  // namespace remoting
    252