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