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