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.
5 // This file implements the common entry point shared by all Chromoting Host
8 #include "remoting/host/host_main.h"
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"
27 #if defined(OS_MACOSX)
28 #include "base/mac/scoped_nsautorelease_pool.h"
29 #endif // defined(OS_MACOSX)
34 #endif // defined(OS_WIN)
38 // Known entry points.
39 int DaemonProcessMain();
40 int DesktopProcessMain();
41 int ElevatedControllerMain();
42 int HostProcessMain();
43 int RdpDesktopSessionMain();
45 const char kElevateSwitchName[] = "elevate";
46 const char kProcessTypeSwitchName[] = "type";
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";
55 const char kExtensionOriginPrefix[] = "chrome-extension://";
59 typedef int (*MainRoutineFn)();
61 // "--help" or "--?" prints the usage message.
62 const char kHelpSwitchName[] = "help";
63 const char kQuestionSwitchName[] = "?";
65 // The command line switch used to get version of the daemon.
66 const char kVersionSwitchName[] = "version";
68 const char kUsageMessage[] =
69 "Usage: %s [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";
81 void Usage(const base::FilePath& program_name) {
82 printf(kUsageMessage, program_name.MaybeAsASCII().c_str());
87 // Runs the binary specified by the command line, elevated.
89 const CommandLine::SwitchMap& switches =
90 CommandLine::ForCurrentProcess()->GetSwitches();
91 CommandLine::StringVector args = CommandLine::ForCurrentProcess()->GetArgs();
93 // Create the child process command line by copying switches from the current
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);
101 for (CommandLine::StringVector::const_iterator i = args.begin();
102 i != args.end(); ++i) {
103 command_line.AppendArgNative(*i);
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();
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;
120 if (!ShellExecuteEx(&info)) {
121 DWORD exit_code = GetLastError();
122 LOG_GETLASTERROR(ERROR) << "Unable to launch '" << binary.value() << "'";
126 return kSuccessExitCode;
129 #else // !defined(OS_WIN)
131 // Fake entry points that exist only on Windows.
132 int DaemonProcessMain() {
134 return kInitializationFailed;
137 int DesktopProcessMain() {
139 return kInitializationFailed;
142 int ElevatedControllerMain() {
144 return kInitializationFailed;
147 int RdpDesktopSessionMain() {
149 return kInitializationFailed;
152 #endif // !defined(OS_WIN)
154 // Select the entry point corresponding to the process type.
155 MainRoutineFn SelectMainRoutine(const std::string& process_type) {
156 MainRoutineFn main_routine = NULL;
158 if (process_type == kProcessTypeHost) {
159 main_routine = &HostProcessMain;
161 } else if (process_type == kProcessTypeDaemon) {
162 main_routine = &DaemonProcessMain;
163 } else if (process_type == kProcessTypeDesktop) {
164 main_routine = &DesktopProcessMain;
165 } else if (process_type == kProcessTypeController) {
166 main_routine = &ElevatedControllerMain;
167 } else if (process_type == kProcessTypeRdpDesktopSession) {
168 main_routine = &RdpDesktopSessionMain;
169 } else if (process_type == kProcessTypeNativeMessagingHost) {
170 main_routine = &NativeMessagingHostMain;
171 #endif // defined(OS_WIN)
179 int HostMain(int argc, char** argv) {
180 #if defined(OS_MACOSX)
181 // Needed so we don't leak objects when threads are created.
182 base::mac::ScopedNSAutoreleasePool pool;
185 CommandLine::Init(argc, argv);
187 // Initialize Breakpad as early as possible. On Mac the command-line needs to
188 // be initialized first, so that the preference for crash-reporting can be
189 // looked up in the config file.
190 #if defined(REMOTING_ENABLE_BREAKPAD)
191 if (IsUsageStatsAllowed()) {
192 InitializeCrashReporting();
194 #endif // defined(REMOTING_ENABLE_BREAKPAD)
196 // This object instance is required by Chrome code (for example,
197 // LazyInstance, MessageLoop).
198 base::AtExitManager exit_manager;
200 // Enable debug logs.
203 // Register and initialize common controls.
205 INITCOMMONCONTROLSEX info;
206 info.dwSize = sizeof(info);
207 info.dwICC = ICC_STANDARD_CLASSES;
208 InitCommonControlsEx(&info);
209 #endif // defined(OS_WIN)
211 // Parse the command line.
212 const CommandLine* command_line = CommandLine::ForCurrentProcess();
213 if (command_line->HasSwitch(kHelpSwitchName) ||
214 command_line->HasSwitch(kQuestionSwitchName)) {
215 Usage(command_line->GetProgram());
216 return kSuccessExitCode;
219 if (command_line->HasSwitch(kVersionSwitchName)) {
220 printf("%s\n", STRINGIZE(VERSION));
221 return kSuccessExitCode;
225 if (command_line->HasSwitch(kElevateSwitchName)) {
226 return RunElevated();
228 #endif // defined(OS_WIN)
230 // Assume the host process by default.
231 std::string process_type = kProcessTypeHost;
232 if (command_line->HasSwitch(kProcessTypeSwitchName)) {
233 process_type = command_line->GetSwitchValueASCII(kProcessTypeSwitchName);
235 // Assume that it is the native messaging host starting if "--type" is
236 // missing and the first argument looks like an origin that represents
238 CommandLine::StringVector args = command_line->GetArgs();
241 std::string origin = UTF16ToUTF8(args[0]);
243 std::string origin = args[0];
245 if (StartsWithASCII(origin, kExtensionOriginPrefix, true)) {
246 process_type = kProcessTypeNativeMessagingHost;
251 MainRoutineFn main_routine = SelectMainRoutine(process_type);
253 fprintf(stderr, "Unknown process type '%s' specified.",
254 process_type.c_str());
255 Usage(command_line->GetProgram());
256 return kUsageExitCode;
259 remoting::LoadResources("");
261 // Invoke the entry point.
262 int exit_code = main_routine();
263 if (exit_code == kUsageExitCode) {
264 Usage(command_line->GetProgram());
267 remoting::UnloadResources();
272 } // namespace remoting