• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Usage(const base::FilePath & program_name)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.
RunElevated()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.
SelectMainRoutine(const std::string & 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 
HostMain(int argc,char ** argv)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