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