Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / host / setup / daemon_controller_delegate_linux.cc
blob30f00ccc625ee9306ecf5fa6c431e1fa2aed3c77
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 base::FilePath GetConfigPath() {
41 std::string filename =
42 "host#" + base::MD5String(net::GetHostName()) + ".json";
43 base::FilePath homedir;
44 PathService::Get(base::DIR_HOME, &homedir);
45 return homedir.Append(".config/chrome-remote-desktop").Append(filename);
48 bool GetScriptPath(base::FilePath* result) {
49 base::FilePath candidate_exe(kDaemonScript);
50 if (access(candidate_exe.value().c_str(), X_OK) == 0) {
51 *result = candidate_exe;
52 return true;
54 return false;
57 bool RunHostScript(const std::vector<std::string>& args) {
58 // As long as we're relying on running an external binary from the
59 // PATH, don't do it as root.
60 if (getuid() == 0) {
61 LOG(ERROR) << "Refusing to run script as root.";
62 return false;
64 base::FilePath script_path;
65 if (!GetScriptPath(&script_path)) {
66 LOG(ERROR) << "GetScriptPath() failed.";
67 return false;
69 base::CommandLine command_line(script_path);
70 for (unsigned int i = 0; i < args.size(); ++i) {
71 command_line.AppendArg(args[i]);
74 std::string output;
75 bool result = base::GetAppOutputAndError(command_line, &output);
76 if (result) {
77 LOG(INFO) << output;
78 } else {
79 LOG(ERROR) << output;
82 return result;
85 } // namespace
87 DaemonControllerDelegateLinux::DaemonControllerDelegateLinux() {
90 DaemonControllerDelegateLinux::~DaemonControllerDelegateLinux() {
93 DaemonController::State DaemonControllerDelegateLinux::GetState() {
94 base::FilePath script_path;
95 if (!GetScriptPath(&script_path)) {
96 LOG(ERROR) << "GetScriptPath() failed.";
97 return DaemonController::STATE_UNKNOWN;
99 base::CommandLine command_line(script_path);
100 command_line.AppendArg("--get-status");
102 std::string status;
103 int exit_code = 0;
104 if (!base::GetAppOutputWithExitCode(command_line, &status, &exit_code) ||
105 exit_code != 0) {
106 LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
107 << "\". Exit code: " << exit_code;
108 return DaemonController::STATE_UNKNOWN;
111 base::TrimWhitespaceASCII(status, base::TRIM_ALL, &status);
113 if (status == "STARTED") {
114 return DaemonController::STATE_STARTED;
115 } else if (status == "STOPPED") {
116 return DaemonController::STATE_STOPPED;
117 } else if (status == "NOT_IMPLEMENTED") {
118 // Chrome Remote Desktop is not currently supported on the underlying Linux
119 // Distro.
120 return DaemonController::STATE_NOT_IMPLEMENTED;
121 } else {
122 LOG(ERROR) << "Unknown status string returned from \""
123 << command_line.GetCommandLineString()
124 << "\": " << status;
125 return DaemonController::STATE_UNKNOWN;
129 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateLinux::GetConfig() {
130 scoped_ptr<base::DictionaryValue> config(
131 HostConfigFromJsonFile(GetConfigPath()));
132 if (!config)
133 return nullptr;
135 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
136 std::string value;
137 if (config->GetString(kHostIdConfigPath, &value)) {
138 result->SetString(kHostIdConfigPath, value);
140 if (config->GetString(kXmppLoginConfigPath, &value)) {
141 result->SetString(kXmppLoginConfigPath, value);
143 return result.Pass();
146 void DaemonControllerDelegateLinux::SetConfigAndStart(
147 scoped_ptr<base::DictionaryValue> config,
148 bool consent,
149 const DaemonController::CompletionCallback& done) {
150 // Add the user to chrome-remote-desktop group first.
151 std::vector<std::string> args;
152 args.push_back("--add-user");
153 if (!RunHostScript(args)) {
154 LOG(ERROR) << "Failed to add user to chrome-remote-desktop group.";
155 done.Run(DaemonController::RESULT_FAILED);
156 return;
159 // Ensure the configuration directory exists.
160 base::FilePath config_dir = GetConfigPath().DirName();
161 if (!base::DirectoryExists(config_dir) &&
162 !base::CreateDirectory(config_dir)) {
163 LOG(ERROR) << "Failed to create config directory " << config_dir.value();
164 done.Run(DaemonController::RESULT_FAILED);
165 return;
168 // Write config.
169 if (!HostConfigToJsonFile(*config, GetConfigPath())) {
170 LOG(ERROR) << "Failed to update config file.";
171 done.Run(DaemonController::RESULT_FAILED);
172 return;
175 // Finally start the host.
176 args.clear();
177 args.push_back("--start");
178 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
179 if (RunHostScript(args))
180 result = DaemonController::RESULT_OK;
182 done.Run(result);
185 void DaemonControllerDelegateLinux::UpdateConfig(
186 scoped_ptr<base::DictionaryValue> config,
187 const DaemonController::CompletionCallback& done) {
188 scoped_ptr<base::DictionaryValue> new_config(
189 HostConfigFromJsonFile(GetConfigPath()));
190 if (new_config)
191 new_config->MergeDictionary(config.get());
192 if (!new_config || !HostConfigToJsonFile(*new_config, GetConfigPath())) {
193 LOG(ERROR) << "Failed to update config file.";
194 done.Run(DaemonController::RESULT_FAILED);
195 return;
198 std::vector<std::string> args;
199 args.push_back("--reload");
200 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
201 if (RunHostScript(args))
202 result = DaemonController::RESULT_OK;
204 done.Run(result);
207 void DaemonControllerDelegateLinux::Stop(
208 const DaemonController::CompletionCallback& done) {
209 std::vector<std::string> args;
210 args.push_back("--stop");
211 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
212 if (RunHostScript(args))
213 result = DaemonController::RESULT_OK;
215 done.Run(result);
218 DaemonController::UsageStatsConsent
219 DaemonControllerDelegateLinux::GetUsageStatsConsent() {
220 // Crash dump collection is not implemented on Linux yet.
221 // http://crbug.com/130678.
222 DaemonController::UsageStatsConsent consent;
223 consent.supported = false;
224 consent.allowed = false;
225 consent.set_by_policy = false;
226 return consent;
229 scoped_refptr<DaemonController> DaemonController::Create() {
230 scoped_ptr<DaemonController::Delegate> delegate(
231 new DaemonControllerDelegateLinux());
232 return new DaemonController(delegate.Pass());
235 } // namespace remoting