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 " --window-id=<id> - Specifies a window to remote," 81 " instead of the whole desktop.\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 base::CommandLine::SwitchMap& switches = 92 base::CommandLine::ForCurrentProcess()->GetSwitches(); 93 base::CommandLine::StringVector args = 94 base::CommandLine::ForCurrentProcess()->GetArgs(); 95 96 // Create the child process command line by copying switches from the current 97 // command line. 98 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); 99 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin(); 100 i != switches.end(); ++i) { 101 if (i->first != kElevateSwitchName) 102 command_line.AppendSwitchNative(i->first, i->second); 103 } 104 for (base::CommandLine::StringVector::const_iterator i = args.begin(); 105 i != args.end(); ++i) { 106 command_line.AppendArgNative(*i); 107 } 108 109 // Get the name of the binary to launch. 110 base::FilePath binary = 111 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( 112 kElevateSwitchName); 113 base::CommandLine::StringType parameters = 114 command_line.GetCommandLineString(); 115 116 // Launch the child process requesting elevation. 117 SHELLEXECUTEINFO info; 118 memset(&info, 0, sizeof(info)); 119 info.cbSize = sizeof(info); 120 info.lpVerb = L"runas"; 121 info.lpFile = binary.value().c_str(); 122 info.lpParameters = parameters.c_str(); 123 info.nShow = SW_SHOWNORMAL; 124 125 if (!ShellExecuteEx(&info)) { 126 DWORD exit_code = GetLastError(); 127 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'"; 128 return exit_code; 129 } 130 131 return kSuccessExitCode; 132 } 133 134 #endif // !defined(OS_WIN) 135 136 // Select the entry point corresponding to the process type. 137 MainRoutineFn SelectMainRoutine(const std::string& process_type) { 138 MainRoutineFn main_routine = NULL; 139 140 if (process_type == kProcessTypeHost) { 141 main_routine = &HostProcessMain; 142 #if defined(OS_WIN) 143 } else if (process_type == kProcessTypeDaemon) { 144 main_routine = &DaemonProcessMain; 145 } else if (process_type == kProcessTypeDesktop) { 146 main_routine = &DesktopProcessMain; 147 } else if (process_type == kProcessTypeController) { 148 main_routine = &ElevatedControllerMain; 149 } else if (process_type == kProcessTypeRdpDesktopSession) { 150 main_routine = &RdpDesktopSessionMain; 151 #endif // defined(OS_WIN) 152 } 153 154 return main_routine; 155 } 156 157 } // namespace 158 159 int HostMain(int argc, char** argv) { 160 #if defined(OS_MACOSX) 161 // Needed so we don't leak objects when threads are created. 162 base::mac::ScopedNSAutoreleasePool pool; 163 #endif 164 165 base::CommandLine::Init(argc, argv); 166 167 // Initialize Breakpad as early as possible. On Mac the command-line needs to 168 // be initialized first, so that the preference for crash-reporting can be 169 // looked up in the config file. 170 #if defined(REMOTING_ENABLE_BREAKPAD) 171 if (IsUsageStatsAllowed()) { 172 InitializeCrashReporting(); 173 } 174 #endif // defined(REMOTING_ENABLE_BREAKPAD) 175 176 // This object instance is required by Chrome code (for example, 177 // LazyInstance, MessageLoop). 178 base::AtExitManager exit_manager; 179 180 // Enable debug logs. 181 InitHostLogging(); 182 183 // Register and initialize common controls. 184 #if defined(OS_WIN) 185 INITCOMMONCONTROLSEX info; 186 info.dwSize = sizeof(info); 187 info.dwICC = ICC_STANDARD_CLASSES; 188 InitCommonControlsEx(&info); 189 #endif // defined(OS_WIN) 190 191 // Parse the command line. 192 const base::CommandLine* command_line = 193 base::CommandLine::ForCurrentProcess(); 194 if (command_line->HasSwitch(kHelpSwitchName) || 195 command_line->HasSwitch(kQuestionSwitchName)) { 196 Usage(command_line->GetProgram()); 197 return kSuccessExitCode; 198 } 199 200 if (command_line->HasSwitch(kVersionSwitchName)) { 201 printf("%s\n", STRINGIZE(VERSION)); 202 return kSuccessExitCode; 203 } 204 205 #if defined(OS_WIN) 206 if (command_line->HasSwitch(kElevateSwitchName)) { 207 return RunElevated(); 208 } 209 #endif // defined(OS_WIN) 210 211 // Assume the host process by default. 212 std::string process_type = kProcessTypeHost; 213 if (command_line->HasSwitch(kProcessTypeSwitchName)) { 214 process_type = command_line->GetSwitchValueASCII(kProcessTypeSwitchName); 215 } 216 217 MainRoutineFn main_routine = SelectMainRoutine(process_type); 218 if (!main_routine) { 219 fprintf(stderr, "Unknown process type '%s' specified.", 220 process_type.c_str()); 221 Usage(command_line->GetProgram()); 222 return kUsageExitCode; 223 } 224 225 // Required to find the ICU data file, used by some file_util routines. 226 base::i18n::InitializeICU(); 227 228 remoting::LoadResources(""); 229 230 // Invoke the entry point. 231 int exit_code = main_routine(); 232 if (exit_code == kUsageExitCode) { 233 Usage(command_line->GetProgram()); 234 } 235 236 remoting::UnloadResources(); 237 238 return exit_code; 239 } 240 241 } // namespace remoting 242 243 #if !defined(OS_WIN) 244 int main(int argc, char** argv) { 245 return remoting::HostMain(argc, argv); 246 } 247 #endif // !defined(OS_WIN) 248