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"
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"
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"
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
;
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.
61 LOG(ERROR
) << "Refusing to run script as root.";
64 base::FilePath script_path
;
65 if (!GetScriptPath(&script_path
)) {
66 LOG(ERROR
) << "GetScriptPath() failed.";
69 base::CommandLine
command_line(script_path
);
70 for (unsigned int i
= 0; i
< args
.size(); ++i
) {
71 command_line
.AppendArg(args
[i
]);
75 bool result
= base::GetAppOutputAndError(command_line
, &output
);
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");
104 if (!base::GetAppOutputWithExitCode(command_line
, &status
, &exit_code
) ||
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
120 return DaemonController::STATE_NOT_IMPLEMENTED
;
122 LOG(ERROR
) << "Unknown status string returned from \""
123 << command_line
.GetCommandLineString()
125 return DaemonController::STATE_UNKNOWN
;
129 scoped_ptr
<base::DictionaryValue
> DaemonControllerDelegateLinux::GetConfig() {
130 scoped_ptr
<base::DictionaryValue
> config(
131 HostConfigFromJsonFile(GetConfigPath()));
135 scoped_ptr
<base::DictionaryValue
> result(new base::DictionaryValue());
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
,
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
);
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
);
169 if (!HostConfigToJsonFile(*config
, GetConfigPath())) {
170 LOG(ERROR
) << "Failed to update config file.";
171 done
.Run(DaemonController::RESULT_FAILED
);
175 // Finally start the host.
177 args
.push_back("--start");
178 DaemonController::AsyncResult result
= DaemonController::RESULT_FAILED
;
179 if (RunHostScript(args
))
180 result
= DaemonController::RESULT_OK
;
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()));
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
);
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
;
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
;
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;
229 scoped_refptr
<DaemonController
> DaemonController::Create() {
230 scoped_ptr
<DaemonController::Delegate
> delegate(
231 new DaemonControllerDelegateLinux());
232 return new DaemonController(delegate
.Pass());
235 } // namespace remoting