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
37namespace remoting {
38
39// Known entry points.
40int HostProcessMain();
41#if defined(OS_WIN)
42int DaemonProcessMain();
43int DesktopProcessMain();
44int ElevatedControllerMain();
45int RdpDesktopSessionMain();
46#endif  // defined(OS_WIN)
47
48const char kElevateSwitchName[] = "elevate";
49const char kProcessTypeSwitchName[] = "type";
50
51const char kProcessTypeController[] = "controller";
52const char kProcessTypeDaemon[] = "daemon";
53const char kProcessTypeDesktop[] = "desktop";
54const char kProcessTypeHost[] = "host";
55const char kProcessTypeRdpDesktopSession[] = "rdp_desktop_session";
56
57namespace {
58
59typedef int (*MainRoutineFn)();
60
61// "--help" or "--?" prints the usage message.
62const char kHelpSwitchName[] = "help";
63const char kQuestionSwitchName[] = "?";
64
65// The command line switch used to get version of the daemon.
66const char kVersionSwitchName[] = "version";
67
68const 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
83void 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.
90int 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.
137MainRoutineFn 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
159int 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)
244int main(int argc, char** argv) {
245  return remoting::HostMain(argc, argv);
246}
247#endif  // !defined(OS_WIN)
248