Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / remoting / host / setup / daemon_controller_delegate_linux.cc
blobb015eda08d143ec4920e26bd8331eb0bf4decc7f
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/kill.h"
22 #include "base/process/launch.h"
23 #include "base/process/process_handle.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_split.h"
26 #include "base/strings/string_util.h"
27 #include "base/thread_task_runner_handle.h"
28 #include "base/values.h"
29 #include "build/build_config.h"
30 #include "net/base/net_util.h"
31 #include "remoting/host/host_config.h"
32 #include "remoting/host/usage_stats_consent.h"
34 namespace remoting {
36 namespace {
38 const char kDaemonScript[] =
39 "/opt/google/chrome-remote-desktop/chrome-remote-desktop";
41 // Timeout for running daemon script. The script itself sets a timeout when
42 // waiting for the host to come online, so the setting here should be at least
43 // as long.
44 const int64 kDaemonTimeoutMs = 60000;
46 // Timeout for commands that require password prompt - 5 minutes.
47 const int64 kSudoTimeoutSeconds = 5 * 60;
49 std::string GetMd5(const std::string& value) {
50 base::MD5Context ctx;
51 base::MD5Init(&ctx);
52 base::MD5Update(&ctx, value);
53 base::MD5Digest digest;
54 base::MD5Final(&digest, &ctx);
55 return base::StringToLowerASCII(base::HexEncode(digest.a, sizeof(digest.a)));
58 base::FilePath GetConfigPath() {
59 std::string filename = "host#" + GetMd5(net::GetHostName()) + ".json";
60 base::FilePath homedir;
61 PathService::Get(base::DIR_HOME, &homedir);
62 return homedir.Append(".config/chrome-remote-desktop").Append(filename);
65 bool GetScriptPath(base::FilePath* result) {
66 base::FilePath candidate_exe(kDaemonScript);
67 if (access(candidate_exe.value().c_str(), X_OK) == 0) {
68 *result = candidate_exe;
69 return true;
71 return false;
74 bool RunHostScriptWithTimeout(
75 const std::vector<std::string>& args,
76 base::TimeDelta timeout,
77 int* exit_code) {
78 DCHECK(exit_code);
80 // As long as we're relying on running an external binary from the
81 // PATH, don't do it as root.
82 if (getuid() == 0) {
83 LOG(ERROR) << "Refusing to run script as root.";
84 return false;
86 base::FilePath script_path;
87 if (!GetScriptPath(&script_path)) {
88 LOG(ERROR) << "GetScriptPath() failed.";
89 return false;
91 base::CommandLine command_line(script_path);
92 for (unsigned int i = 0; i < args.size(); ++i) {
93 command_line.AppendArg(args[i]);
96 // Redirect the child's stdout to the parent's stderr. In the case where this
97 // parent process is a Native Messaging host, its stdout is used to send
98 // messages to the web-app.
99 base::FileHandleMappingVector fds_to_remap;
100 fds_to_remap.push_back(std::pair<int, int>(STDERR_FILENO, STDOUT_FILENO));
101 base::LaunchOptions options;
102 options.fds_to_remap = &fds_to_remap;
104 #if !defined(OS_CHROMEOS)
105 options.allow_new_privs = true;
106 #endif
108 base::Process process = base::LaunchProcess(command_line, options);
109 if (!process.IsValid()) {
110 LOG(ERROR) << "Failed to run command: "
111 << command_line.GetCommandLineString();
112 return false;
115 if (!process.WaitForExitWithTimeout(timeout, exit_code)) {
116 base::KillProcess(process.Handle(), 0, false);
117 LOG(ERROR) << "Timeout exceeded for command: "
118 << command_line.GetCommandLineString();
119 return false;
122 return true;
125 bool RunHostScript(const std::vector<std::string>& args, int* exit_code) {
126 return RunHostScriptWithTimeout(
127 args, base::TimeDelta::FromMilliseconds(kDaemonTimeoutMs), exit_code);
130 } // namespace
132 DaemonControllerDelegateLinux::DaemonControllerDelegateLinux() {
135 DaemonControllerDelegateLinux::~DaemonControllerDelegateLinux() {
138 DaemonController::State DaemonControllerDelegateLinux::GetState() {
139 base::FilePath script_path;
140 if (!GetScriptPath(&script_path)) {
141 LOG(ERROR) << "GetScriptPath() failed.";
142 return DaemonController::STATE_UNKNOWN;
144 base::CommandLine command_line(script_path);
145 command_line.AppendArg("--get-status");
147 std::string status;
148 int exit_code = 0;
149 if (!base::GetAppOutputWithExitCode(command_line, &status, &exit_code) ||
150 exit_code != 0) {
151 LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
152 << "\". Exit code: " << exit_code;
153 return DaemonController::STATE_UNKNOWN;
156 base::TrimWhitespaceASCII(status, base::TRIM_ALL, &status);
158 if (status == "STARTED") {
159 return DaemonController::STATE_STARTED;
160 } else if (status == "STOPPED") {
161 return DaemonController::STATE_STOPPED;
162 } else if (status == "NOT_IMPLEMENTED") {
163 // Chrome Remote Desktop is not currently supported on the underlying Linux
164 // Distro.
165 return DaemonController::STATE_NOT_IMPLEMENTED;
166 } else {
167 LOG(ERROR) << "Unknown status string returned from \""
168 << command_line.GetCommandLineString()
169 << "\": " << status;
170 return DaemonController::STATE_UNKNOWN;
174 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateLinux::GetConfig() {
175 scoped_ptr<base::DictionaryValue> config(
176 HostConfigFromJsonFile(GetConfigPath()));
177 if (!config)
178 return nullptr;
180 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
181 std::string value;
182 if (config->GetString(kHostIdConfigPath, &value)) {
183 result->SetString(kHostIdConfigPath, value);
185 if (config->GetString(kXmppLoginConfigPath, &value)) {
186 result->SetString(kXmppLoginConfigPath, value);
188 return result.Pass();
191 void DaemonControllerDelegateLinux::SetConfigAndStart(
192 scoped_ptr<base::DictionaryValue> config,
193 bool consent,
194 const DaemonController::CompletionCallback& done) {
195 // Add the user to chrome-remote-desktop group first.
196 std::vector<std::string> args;
197 args.push_back("--add-user");
198 int exit_code;
199 if (!RunHostScriptWithTimeout(
200 args, base::TimeDelta::FromSeconds(kSudoTimeoutSeconds),
201 &exit_code) ||
202 exit_code != 0) {
203 LOG(ERROR) << "Failed to add user to chrome-remote-desktop group.";
204 done.Run(DaemonController::RESULT_FAILED);
205 return;
208 // Ensure the configuration directory exists.
209 base::FilePath config_dir = GetConfigPath().DirName();
210 if (!base::DirectoryExists(config_dir) &&
211 !base::CreateDirectory(config_dir)) {
212 LOG(ERROR) << "Failed to create config directory " << config_dir.value();
213 done.Run(DaemonController::RESULT_FAILED);
214 return;
217 // Write config.
218 if (!HostConfigToJsonFile(*config, GetConfigPath())) {
219 LOG(ERROR) << "Failed to update config file.";
220 done.Run(DaemonController::RESULT_FAILED);
221 return;
224 // Finally start the host.
225 args.clear();
226 args.push_back("--start");
227 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
228 if (RunHostScript(args, &exit_code) && (exit_code == 0))
229 result = DaemonController::RESULT_OK;
231 done.Run(result);
234 void DaemonControllerDelegateLinux::UpdateConfig(
235 scoped_ptr<base::DictionaryValue> config,
236 const DaemonController::CompletionCallback& done) {
237 scoped_ptr<base::DictionaryValue> new_config(
238 HostConfigFromJsonFile(GetConfigPath()));
239 if (new_config)
240 new_config->MergeDictionary(config.get());
241 if (!new_config || !HostConfigToJsonFile(*new_config, GetConfigPath())) {
242 LOG(ERROR) << "Failed to update config file.";
243 done.Run(DaemonController::RESULT_FAILED);
244 return;
247 std::vector<std::string> args;
248 args.push_back("--reload");
249 int exit_code = 0;
250 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
251 if (RunHostScript(args, &exit_code) && (exit_code == 0))
252 result = DaemonController::RESULT_OK;
254 done.Run(result);
257 void DaemonControllerDelegateLinux::Stop(
258 const DaemonController::CompletionCallback& done) {
259 std::vector<std::string> args;
260 args.push_back("--stop");
261 int exit_code = 0;
262 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
263 if (RunHostScript(args, &exit_code) && (exit_code == 0))
264 result = DaemonController::RESULT_OK;
266 done.Run(result);
269 DaemonController::UsageStatsConsent
270 DaemonControllerDelegateLinux::GetUsageStatsConsent() {
271 // Crash dump collection is not implemented on Linux yet.
272 // http://crbug.com/130678.
273 DaemonController::UsageStatsConsent consent;
274 consent.supported = false;
275 consent.allowed = false;
276 consent.set_by_policy = false;
277 return consent;
280 scoped_refptr<DaemonController> DaemonController::Create() {
281 scoped_ptr<DaemonController::Delegate> delegate(
282 new DaemonControllerDelegateLinux());
283 return new DaemonController(delegate.Pass());
286 } // namespace remoting