Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / host / setup / daemon_controller_delegate_linux.cc
blob4928b8614f43d630864af29a2cd1f64b1df656c6
1 // Copyright 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 #include "remoting/host/setup/daemon_controller_delegate_linux.h"
7 #include <unistd.h>
9 #include "base/base_paths.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/environment.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/json/json_writer.h"
18 #include "base/logging.h"
19 #include "base/md5.h"
20 #include "base/path_service.h"
21 #include "base/process/launch.h"
22 #include "base/process/process.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_util.h"
26 #include "base/thread_task_runner_handle.h"
27 #include "base/values.h"
28 #include "build/build_config.h"
29 #include "net/base/net_util.h"
30 #include "remoting/host/host_config.h"
31 #include "remoting/host/usage_stats_consent.h"
33 namespace remoting {
35 namespace {
37 const char kDaemonScript[] =
38 "/opt/google/chrome-remote-desktop/chrome-remote-desktop";
40 // Timeout for running daemon script. The script itself sets a timeout when
41 // waiting for the host to come online, so the setting here should be at least
42 // as long.
43 const int64 kDaemonTimeoutMs = 60000;
45 // Timeout for commands that require password prompt - 5 minutes.
46 const int64 kSudoTimeoutSeconds = 5 * 60;
48 base::FilePath GetConfigPath() {
49 std::string filename =
50 "host#" + base::MD5String(net::GetHostName()) + ".json";
51 base::FilePath homedir;
52 PathService::Get(base::DIR_HOME, &homedir);
53 return homedir.Append(".config/chrome-remote-desktop").Append(filename);
56 bool GetScriptPath(base::FilePath* result) {
57 base::FilePath candidate_exe(kDaemonScript);
58 if (access(candidate_exe.value().c_str(), X_OK) == 0) {
59 *result = candidate_exe;
60 return true;
62 return false;
65 bool RunHostScriptWithTimeout(
66 const std::vector<std::string>& args,
67 base::TimeDelta timeout,
68 int* exit_code) {
69 DCHECK(exit_code);
71 // As long as we're relying on running an external binary from the
72 // PATH, don't do it as root.
73 if (getuid() == 0) {
74 LOG(ERROR) << "Refusing to run script as root.";
75 return false;
77 base::FilePath script_path;
78 if (!GetScriptPath(&script_path)) {
79 LOG(ERROR) << "GetScriptPath() failed.";
80 return false;
82 base::CommandLine command_line(script_path);
83 for (unsigned int i = 0; i < args.size(); ++i) {
84 command_line.AppendArg(args[i]);
87 // Redirect the child's stdout to the parent's stderr. In the case where this
88 // parent process is a Native Messaging host, its stdout is used to send
89 // messages to the web-app.
90 base::FileHandleMappingVector fds_to_remap;
91 fds_to_remap.push_back(std::pair<int, int>(STDERR_FILENO, STDOUT_FILENO));
92 base::LaunchOptions options;
93 options.fds_to_remap = &fds_to_remap;
95 #if !defined(OS_CHROMEOS)
96 options.allow_new_privs = true;
97 #endif
99 base::Process process = base::LaunchProcess(command_line, options);
100 if (!process.IsValid()) {
101 LOG(ERROR) << "Failed to run command: "
102 << command_line.GetCommandLineString();
103 return false;
106 if (!process.WaitForExitWithTimeout(timeout, exit_code)) {
107 process.Terminate(0, false);
108 LOG(ERROR) << "Timeout exceeded for command: "
109 << command_line.GetCommandLineString();
110 return false;
113 return true;
116 bool RunHostScript(const std::vector<std::string>& args, int* exit_code) {
117 return RunHostScriptWithTimeout(
118 args, base::TimeDelta::FromMilliseconds(kDaemonTimeoutMs), exit_code);
121 } // namespace
123 DaemonControllerDelegateLinux::DaemonControllerDelegateLinux() {
126 DaemonControllerDelegateLinux::~DaemonControllerDelegateLinux() {
129 DaemonController::State DaemonControllerDelegateLinux::GetState() {
130 base::FilePath script_path;
131 if (!GetScriptPath(&script_path)) {
132 LOG(ERROR) << "GetScriptPath() failed.";
133 return DaemonController::STATE_UNKNOWN;
135 base::CommandLine command_line(script_path);
136 command_line.AppendArg("--get-status");
138 std::string status;
139 int exit_code = 0;
140 if (!base::GetAppOutputWithExitCode(command_line, &status, &exit_code) ||
141 exit_code != 0) {
142 LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
143 << "\". Exit code: " << exit_code;
144 return DaemonController::STATE_UNKNOWN;
147 base::TrimWhitespaceASCII(status, base::TRIM_ALL, &status);
149 if (status == "STARTED") {
150 return DaemonController::STATE_STARTED;
151 } else if (status == "STOPPED") {
152 return DaemonController::STATE_STOPPED;
153 } else if (status == "NOT_IMPLEMENTED") {
154 // Chrome Remote Desktop is not currently supported on the underlying Linux
155 // Distro.
156 return DaemonController::STATE_NOT_IMPLEMENTED;
157 } else {
158 LOG(ERROR) << "Unknown status string returned from \""
159 << command_line.GetCommandLineString()
160 << "\": " << status;
161 return DaemonController::STATE_UNKNOWN;
165 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateLinux::GetConfig() {
166 scoped_ptr<base::DictionaryValue> config(
167 HostConfigFromJsonFile(GetConfigPath()));
168 if (!config)
169 return nullptr;
171 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
172 std::string value;
173 if (config->GetString(kHostIdConfigPath, &value)) {
174 result->SetString(kHostIdConfigPath, value);
176 if (config->GetString(kXmppLoginConfigPath, &value)) {
177 result->SetString(kXmppLoginConfigPath, value);
179 return result.Pass();
182 void DaemonControllerDelegateLinux::SetConfigAndStart(
183 scoped_ptr<base::DictionaryValue> config,
184 bool consent,
185 const DaemonController::CompletionCallback& done) {
186 // Add the user to chrome-remote-desktop group first.
187 std::vector<std::string> args;
188 args.push_back("--add-user");
189 int exit_code;
190 if (!RunHostScriptWithTimeout(
191 args, base::TimeDelta::FromSeconds(kSudoTimeoutSeconds),
192 &exit_code) ||
193 exit_code != 0) {
194 LOG(ERROR) << "Failed to add user to chrome-remote-desktop group.";
195 done.Run(DaemonController::RESULT_FAILED);
196 return;
199 // Ensure the configuration directory exists.
200 base::FilePath config_dir = GetConfigPath().DirName();
201 if (!base::DirectoryExists(config_dir) &&
202 !base::CreateDirectory(config_dir)) {
203 LOG(ERROR) << "Failed to create config directory " << config_dir.value();
204 done.Run(DaemonController::RESULT_FAILED);
205 return;
208 // Write config.
209 if (!HostConfigToJsonFile(*config, GetConfigPath())) {
210 LOG(ERROR) << "Failed to update config file.";
211 done.Run(DaemonController::RESULT_FAILED);
212 return;
215 // Finally start the host.
216 args.clear();
217 args.push_back("--start");
218 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
219 if (RunHostScript(args, &exit_code) && (exit_code == 0))
220 result = DaemonController::RESULT_OK;
222 done.Run(result);
225 void DaemonControllerDelegateLinux::UpdateConfig(
226 scoped_ptr<base::DictionaryValue> config,
227 const DaemonController::CompletionCallback& done) {
228 scoped_ptr<base::DictionaryValue> new_config(
229 HostConfigFromJsonFile(GetConfigPath()));
230 if (new_config)
231 new_config->MergeDictionary(config.get());
232 if (!new_config || !HostConfigToJsonFile(*new_config, GetConfigPath())) {
233 LOG(ERROR) << "Failed to update config file.";
234 done.Run(DaemonController::RESULT_FAILED);
235 return;
238 std::vector<std::string> args;
239 args.push_back("--reload");
240 int exit_code = 0;
241 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
242 if (RunHostScript(args, &exit_code) && (exit_code == 0))
243 result = DaemonController::RESULT_OK;
245 done.Run(result);
248 void DaemonControllerDelegateLinux::Stop(
249 const DaemonController::CompletionCallback& done) {
250 std::vector<std::string> args;
251 args.push_back("--stop");
252 int exit_code = 0;
253 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
254 if (RunHostScript(args, &exit_code) && (exit_code == 0))
255 result = DaemonController::RESULT_OK;
257 done.Run(result);
260 DaemonController::UsageStatsConsent
261 DaemonControllerDelegateLinux::GetUsageStatsConsent() {
262 // Crash dump collection is not implemented on Linux yet.
263 // http://crbug.com/130678.
264 DaemonController::UsageStatsConsent consent;
265 consent.supported = false;
266 consent.allowed = false;
267 consent.set_by_policy = false;
268 return consent;
271 scoped_refptr<DaemonController> DaemonController::Create() {
272 scoped_ptr<DaemonController::Delegate> delegate(
273 new DaemonControllerDelegateLinux());
274 return new DaemonController(delegate.Pass());
277 } // namespace remoting