Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: remoting/host/setup/daemon_controller_delegate_linux.cc

Issue 23606019: Refactor the daemon controller so that the callbacks are called on the caller thread. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/setup/daemon_controller.h" 5 #include "remoting/host/setup/daemon_controller_delegate_linux.h"
6 6
7 #include <unistd.h> 7 #include <unistd.h>
8 8
9 #include "base/basictypes.h" 9 #include "base/basictypes.h"
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/compiler_specific.h" 12 #include "base/compiler_specific.h"
13 #include "base/environment.h" 13 #include "base/environment.h"
14 #include "base/file_util.h" 14 #include "base/file_util.h"
15 #include "base/files/file_path.h" 15 #include "base/files/file_path.h"
16 #include "base/json/json_writer.h" 16 #include "base/json/json_writer.h"
17 #include "base/logging.h" 17 #include "base/logging.h"
18 #include "base/md5.h" 18 #include "base/md5.h"
19 #include "base/process/kill.h" 19 #include "base/process/kill.h"
20 #include "base/process/launch.h" 20 #include "base/process/launch.h"
21 #include "base/process/process_handle.h" 21 #include "base/process/process_handle.h"
22 #include "base/strings/string_number_conversions.h" 22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_split.h" 23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h" 24 #include "base/strings/string_util.h"
25 #include "base/threading/thread.h" 25 #include "base/thread_task_runner_handle.h"
26 #include "base/values.h" 26 #include "base/values.h"
27 #include "net/base/net_util.h" 27 #include "net/base/net_util.h"
28 #include "remoting/host/host_config.h" 28 #include "remoting/host/host_config.h"
29 #include "remoting/host/json_host_config.h" 29 #include "remoting/host/json_host_config.h"
30 #include "remoting/host/usage_stats_consent.h" 30 #include "remoting/host/usage_stats_consent.h"
31 31
32 namespace remoting { 32 namespace remoting {
33 33
34 namespace { 34 namespace {
35 35
36 const char kDaemonScript[] = 36 const char kDaemonScript[] =
37 "/opt/google/chrome-remote-desktop/chrome-remote-desktop"; 37 "/opt/google/chrome-remote-desktop/chrome-remote-desktop";
38 38
39 // Timeout for running daemon script. The script itself sets a timeout when 39 // Timeout for running daemon script. The script itself sets a timeout when
40 // waiting for the host to come online, so the setting here should be at least 40 // waiting for the host to come online, so the setting here should be at least
41 // as long. 41 // as long.
42 const int64 kDaemonTimeoutMs = 60000; 42 const int64 kDaemonTimeoutMs = 60000;
43 43
44 // Timeout for commands that require password prompt - 5 minutes. 44 // Timeout for commands that require password prompt - 5 minutes.
45 const int64 kSudoTimeoutSeconds = 5 * 60; 45 const int64 kSudoTimeoutSeconds = 5 * 60;
46 46
47 void Dummy() {}
Sergey Ulanov 2013/09/09 18:12:31 not used anywhere
alexeypa (please no reviews) 2013/09/09 19:30:57 Done.
48
47 std::string GetMd5(const std::string& value) { 49 std::string GetMd5(const std::string& value) {
48 base::MD5Context ctx; 50 base::MD5Context ctx;
49 base::MD5Init(&ctx); 51 base::MD5Init(&ctx);
50 base::MD5Update(&ctx, value); 52 base::MD5Update(&ctx, value);
51 base::MD5Digest digest; 53 base::MD5Digest digest;
52 base::MD5Final(&digest, &ctx); 54 base::MD5Final(&digest, &ctx);
53 return StringToLowerASCII(base::HexEncode(digest.a, sizeof(digest.a))); 55 return StringToLowerASCII(base::HexEncode(digest.a, sizeof(digest.a)));
54 } 56 }
55 57
56 class DaemonControllerLinux : public remoting::DaemonController { 58 base::FilePath GetConfigPath() {
57 public: 59 std::string filename = "host#" + GetMd5(net::GetHostName()) + ".json";
58 DaemonControllerLinux(); 60 return file_util::GetHomeDir().
59 61 Append(".config/chrome-remote-desktop").Append(filename);
60 virtual State GetState() OVERRIDE;
61 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE;
62 virtual void SetConfigAndStart(
63 scoped_ptr<base::DictionaryValue> config,
64 bool consent,
65 const CompletionCallback& done) OVERRIDE;
66 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config,
67 const CompletionCallback& done_callback) OVERRIDE;
68 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE;
69 virtual void SetWindow(void* window_handle) OVERRIDE;
70 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE;
71 virtual void GetUsageStatsConsent(
72 const GetUsageStatsConsentCallback& done) OVERRIDE;
73
74 private:
75 base::FilePath GetConfigPath();
76
77 void DoGetConfig(const GetConfigCallback& callback);
78 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config,
79 const CompletionCallback& done);
80 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config,
81 const CompletionCallback& done_callback);
82 void DoStop(const CompletionCallback& done_callback);
83 void DoGetVersion(const GetVersionCallback& done_callback);
84
85 base::Thread file_io_thread_;
86
87 DISALLOW_COPY_AND_ASSIGN(DaemonControllerLinux);
88 };
89
90 DaemonControllerLinux::DaemonControllerLinux()
91 : file_io_thread_("DaemonControllerFileIO") {
92 file_io_thread_.Start();
93 } 62 }
94 63
95 static bool GetScriptPath(base::FilePath* result) { 64 bool GetScriptPath(base::FilePath* result) {
96 base::FilePath candidate_exe(kDaemonScript); 65 base::FilePath candidate_exe(kDaemonScript);
97 if (access(candidate_exe.value().c_str(), X_OK) == 0) { 66 if (access(candidate_exe.value().c_str(), X_OK) == 0) {
98 *result = candidate_exe; 67 *result = candidate_exe;
99 return true; 68 return true;
100 } 69 }
101 return false; 70 return false;
102 } 71 }
103 72
104 static bool RunHostScriptWithTimeout( 73 bool RunHostScriptWithTimeout(
105 const std::vector<std::string>& args, 74 const std::vector<std::string>& args,
106 base::TimeDelta timeout, 75 base::TimeDelta timeout,
107 int* exit_code) { 76 int* exit_code) {
108 DCHECK(exit_code); 77 DCHECK(exit_code);
109 78
110 // As long as we're relying on running an external binary from the 79 // As long as we're relying on running an external binary from the
111 // PATH, don't do it as root. 80 // PATH, don't do it as root.
112 if (getuid() == 0) { 81 if (getuid() == 0) {
113 LOG(ERROR) << "Refusing to run script as root."; 82 LOG(ERROR) << "Refusing to run script as root.";
114 return false; 83 return false;
(...skipping 25 matching lines...) Expand all
140 if (!base::WaitForExitCodeWithTimeout(process_handle, exit_code, timeout)) { 109 if (!base::WaitForExitCodeWithTimeout(process_handle, exit_code, timeout)) {
141 base::KillProcess(process_handle, 0, false); 110 base::KillProcess(process_handle, 0, false);
142 LOG(ERROR) << "Timeout exceeded for command: " 111 LOG(ERROR) << "Timeout exceeded for command: "
143 << command_line.GetCommandLineString(); 112 << command_line.GetCommandLineString();
144 return false; 113 return false;
145 } 114 }
146 115
147 return true; 116 return true;
148 } 117 }
149 118
150 static bool RunHostScript(const std::vector<std::string>& args, 119 bool RunHostScript(const std::vector<std::string>& args,
151 int* exit_code) { 120 int* exit_code) {
152 return RunHostScriptWithTimeout( 121 return RunHostScriptWithTimeout(
153 args, base::TimeDelta::FromMilliseconds(kDaemonTimeoutMs), exit_code); 122 args, base::TimeDelta::FromMilliseconds(kDaemonTimeoutMs), exit_code);
154 } 123 }
155 124
156 remoting::DaemonController::State DaemonControllerLinux::GetState() { 125 } // namespace
126
127 DaemonControllerDelegateLinux::DaemonControllerDelegateLinux() {
128 }
129
130 DaemonControllerDelegateLinux::~DaemonControllerDelegateLinux() {
131 }
132
133 DaemonController::State DaemonControllerDelegateLinux::GetState() {
157 std::vector<std::string> args; 134 std::vector<std::string> args;
158 args.push_back("--check-running"); 135 args.push_back("--check-running");
159 int exit_code = 0; 136 int exit_code = 0;
160 if (!RunHostScript(args, &exit_code)) { 137 if (!RunHostScript(args, &exit_code)) {
161 // TODO(jamiewalch): When we have a good story for installing, return 138 // TODO(jamiewalch): When we have a good story for installing, return
162 // NOT_INSTALLED rather than NOT_IMPLEMENTED (the former suppresses 139 // NOT_INSTALLED rather than NOT_IMPLEMENTED (the former suppresses
163 // the relevant UI in the web-app). 140 // the relevant UI in the web-app).
164 return remoting::DaemonController::STATE_NOT_IMPLEMENTED; 141 return DaemonController::STATE_NOT_IMPLEMENTED;
165 } 142 }
166 143
167 if (exit_code == 0) { 144 if (exit_code == 0) {
168 return remoting::DaemonController::STATE_STARTED; 145 return DaemonController::STATE_STARTED;
169 } else { 146 } else {
170 return remoting::DaemonController::STATE_STOPPED; 147 return DaemonController::STATE_STOPPED;
171 } 148 }
172 } 149 }
173 150
174 void DaemonControllerLinux::GetConfig(const GetConfigCallback& callback) { 151 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateLinux::GetConfig() {
175 // base::Unretained() is safe because we control lifetime of the thread.
176 file_io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
177 &DaemonControllerLinux::DoGetConfig, base::Unretained(this), callback));
178 }
179
180 void DaemonControllerLinux::GetUsageStatsConsent(
181 const GetUsageStatsConsentCallback& done) {
182 // Crash dump collection is not implemented on Linux yet.
183 // http://crbug.com/130678.
184 done.Run(false, false, false);
185 }
186
187 void DaemonControllerLinux::SetConfigAndStart(
188 scoped_ptr<base::DictionaryValue> config,
189 bool /* consent */,
190 const CompletionCallback& done) {
191 // base::Unretained() is safe because we control lifetime of the thread.
192 file_io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
193 &DaemonControllerLinux::DoSetConfigAndStart, base::Unretained(this),
194 base::Passed(&config), done));
195 }
196
197 void DaemonControllerLinux::UpdateConfig(
198 scoped_ptr<base::DictionaryValue> config,
199 const CompletionCallback& done_callback) {
200 file_io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
201 &DaemonControllerLinux::DoUpdateConfig, base::Unretained(this),
202 base::Passed(&config), done_callback));
203 }
204
205 void DaemonControllerLinux::Stop(const CompletionCallback& done_callback) {
206 file_io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
207 &DaemonControllerLinux::DoStop, base::Unretained(this),
208 done_callback));
209 }
210
211 void DaemonControllerLinux::SetWindow(void* window_handle) {
212 // noop
213 }
214
215 void DaemonControllerLinux::GetVersion(
216 const GetVersionCallback& done_callback) {
217 file_io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
218 &DaemonControllerLinux::DoGetVersion, base::Unretained(this),
219 done_callback));
220 }
221
222 base::FilePath DaemonControllerLinux::GetConfigPath() {
223 std::string filename = "host#" + GetMd5(net::GetHostName()) + ".json";
224 return file_util::GetHomeDir().
225 Append(".config/chrome-remote-desktop").Append(filename);
226 }
227
228 void DaemonControllerLinux::DoGetConfig(const GetConfigCallback& callback) {
229 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); 152 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
230 153
231 if (GetState() != remoting::DaemonController::STATE_NOT_IMPLEMENTED) { 154 if (GetState() != DaemonController::STATE_NOT_IMPLEMENTED) {
232 JsonHostConfig config(GetConfigPath()); 155 JsonHostConfig config(GetConfigPath());
233 if (config.Read()) { 156 if (config.Read()) {
234 std::string value; 157 std::string value;
235 if (config.GetString(kHostIdConfigPath, &value)) { 158 if (config.GetString(kHostIdConfigPath, &value)) {
236 result->SetString(kHostIdConfigPath, value); 159 result->SetString(kHostIdConfigPath, value);
237 } 160 }
238 if (config.GetString(kXmppLoginConfigPath, &value)) { 161 if (config.GetString(kXmppLoginConfigPath, &value)) {
239 result->SetString(kXmppLoginConfigPath, value); 162 result->SetString(kXmppLoginConfigPath, value);
240 } 163 }
241 } else { 164 } else {
242 result.reset(); // Return NULL in case of error. 165 result.reset(); // Return NULL in case of error.
243 } 166 }
244 } 167 }
245 168
246 callback.Run(result.Pass()); 169 return result.Pass();
247 } 170 }
248 171
249 void DaemonControllerLinux::DoSetConfigAndStart( 172 void DaemonControllerDelegateLinux::SetConfigAndStart(
250 scoped_ptr<base::DictionaryValue> config, 173 scoped_ptr<base::DictionaryValue> config,
251 const CompletionCallback& done_callback) { 174 bool consent,
252 175 const DaemonController::CompletionCallback& done) {
253 // Add the user to chrome-remote-desktop group first. 176 // Add the user to chrome-remote-desktop group first.
254 std::vector<std::string> args; 177 std::vector<std::string> args;
255 args.push_back("--add-user"); 178 args.push_back("--add-user");
256 int exit_code; 179 int exit_code;
257 if (!RunHostScriptWithTimeout( 180 if (!RunHostScriptWithTimeout(
258 args, base::TimeDelta::FromSeconds(kSudoTimeoutSeconds), 181 args, base::TimeDelta::FromSeconds(kSudoTimeoutSeconds),
259 &exit_code) || 182 &exit_code) ||
260 exit_code != 0) { 183 exit_code != 0) {
261 LOG(ERROR) << "Failed to add user to chrome-remote-desktop group."; 184 LOG(ERROR) << "Failed to add user to chrome-remote-desktop group.";
262 done_callback.Run(RESULT_FAILED); 185 done.Run(DaemonController::RESULT_FAILED);
263 return; 186 return;
264 } 187 }
265 188
266 // Ensure the configuration directory exists. 189 // Ensure the configuration directory exists.
267 base::FilePath config_dir = GetConfigPath().DirName(); 190 base::FilePath config_dir = GetConfigPath().DirName();
268 if (!base::DirectoryExists(config_dir) && 191 if (!base::DirectoryExists(config_dir) &&
269 !file_util::CreateDirectory(config_dir)) { 192 !file_util::CreateDirectory(config_dir)) {
270 LOG(ERROR) << "Failed to create config directory " << config_dir.value(); 193 LOG(ERROR) << "Failed to create config directory " << config_dir.value();
271 done_callback.Run(RESULT_FAILED); 194 done.Run(DaemonController::RESULT_FAILED);
272 return; 195 return;
273 } 196 }
274 197
275 // Write config. 198 // Write config.
276 JsonHostConfig config_file(GetConfigPath()); 199 JsonHostConfig config_file(GetConfigPath());
277 if (!config_file.CopyFrom(config.get()) || 200 if (!config_file.CopyFrom(config.get()) ||
278 !config_file.Save()) { 201 !config_file.Save()) {
279 LOG(ERROR) << "Failed to update config file."; 202 LOG(ERROR) << "Failed to update config file.";
280 done_callback.Run(RESULT_FAILED); 203 done.Run(DaemonController::RESULT_FAILED);
281 return; 204 return;
282 } 205 }
283 206
284 // Finally start the host. 207 // Finally start the host.
285 args.clear(); 208 args.clear();
286 args.push_back("--start"); 209 args.push_back("--start");
287 AsyncResult result; 210 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
288 if (RunHostScript(args, &exit_code)) { 211 if (RunHostScript(args, &exit_code) && (exit_code == 0))
289 result = (exit_code == 0) ? RESULT_OK : RESULT_FAILED; 212 result = DaemonController::RESULT_OK;
290 } else { 213
291 result = RESULT_FAILED; 214 done.Run(result);
292 }
293 done_callback.Run(result);
294 } 215 }
295 216
296 void DaemonControllerLinux::DoUpdateConfig( 217 void DaemonControllerDelegateLinux::UpdateConfig(
297 scoped_ptr<base::DictionaryValue> config, 218 scoped_ptr<base::DictionaryValue> config,
298 const CompletionCallback& done_callback) { 219 const DaemonController::CompletionCallback& done) {
299 JsonHostConfig config_file(GetConfigPath()); 220 JsonHostConfig config_file(GetConfigPath());
300 if (!config_file.Read() || 221 if (!config_file.Read() ||
301 !config_file.CopyFrom(config.get()) || 222 !config_file.CopyFrom(config.get()) ||
302 !config_file.Save()) { 223 !config_file.Save()) {
303 LOG(ERROR) << "Failed to update config file."; 224 LOG(ERROR) << "Failed to update config file.";
304 done_callback.Run(RESULT_FAILED); 225 done.Run(DaemonController::RESULT_FAILED);
305 return; 226 return;
306 } 227 }
307 228
308 std::vector<std::string> args; 229 std::vector<std::string> args;
309 args.push_back("--reload"); 230 args.push_back("--reload");
310 AsyncResult result; 231 int exit_code = 0;
311 int exit_code; 232 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
312 if (RunHostScript(args, &exit_code)) { 233 if (RunHostScript(args, &exit_code) && (exit_code == 0))
313 result = (exit_code == 0) ? RESULT_OK : RESULT_FAILED; 234 result = DaemonController::RESULT_OK;
314 } else {
315 result = RESULT_FAILED;
316 }
317 235
318 done_callback.Run(result); 236 done.Run(result);
319 } 237 }
320 238
321 void DaemonControllerLinux::DoStop(const CompletionCallback& done_callback) { 239 void DaemonControllerDelegateLinux::Stop(
240 const DaemonController::CompletionCallback& done) {
322 std::vector<std::string> args; 241 std::vector<std::string> args;
323 args.push_back("--stop"); 242 args.push_back("--stop");
324 int exit_code = 0; 243 int exit_code = 0;
325 AsyncResult result; 244 DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
326 if (RunHostScript(args, &exit_code)) { 245 if (RunHostScript(args, &exit_code) && (exit_code == 0))
327 result = (exit_code == 0) ? RESULT_OK : RESULT_FAILED; 246 result = DaemonController::RESULT_OK;
328 } else { 247
329 result = RESULT_FAILED; 248 done.Run(result);
330 }
331 done_callback.Run(result);
332 } 249 }
333 250
334 void DaemonControllerLinux::DoGetVersion( 251 void DaemonControllerDelegateLinux::SetWindow(void* window_handle) {
335 const GetVersionCallback& done_callback) { 252 // noop
253 }
254
255 std::string DaemonControllerDelegateLinux::GetVersion() {
336 base::FilePath script_path; 256 base::FilePath script_path;
337 if (!GetScriptPath(&script_path)) { 257 if (!GetScriptPath(&script_path)) {
338 done_callback.Run(std::string()); 258 return std::string();
339 return;
340 } 259 }
341 CommandLine command_line(script_path); 260 CommandLine command_line(script_path);
342 command_line.AppendArg("--host-version"); 261 command_line.AppendArg("--host-version");
343 262
344 std::string version; 263 std::string version;
345 int exit_code = 0; 264 int exit_code = 0;
346 int result = 265 int result =
347 base::GetAppOutputWithExitCode(command_line, &version, &exit_code); 266 base::GetAppOutputWithExitCode(command_line, &version, &exit_code);
348 if (!result || exit_code != 0) { 267 if (!result || exit_code != 0) {
349 LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString() 268 LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
350 << "\". Exit code: " << exit_code; 269 << "\". Exit code: " << exit_code;
351 done_callback.Run(std::string()); 270 return std::string();
352 return;
353 } 271 }
354 272
355 TrimWhitespaceASCII(version, TRIM_ALL, &version); 273 TrimWhitespaceASCII(version, TRIM_ALL, &version);
356 if (!ContainsOnlyChars(version, "0123456789.")) { 274 if (!ContainsOnlyChars(version, "0123456789.")) {
357 LOG(ERROR) << "Received invalid host version number: " << version; 275 LOG(ERROR) << "Received invalid host version number: " << version;
358 done_callback.Run(std::string()); 276 return std::string();
359 return;
360 } 277 }
361 278
362 done_callback.Run(version); 279 return version;
363 } 280 }
364 281
365 } // namespace 282 DaemonController::UsageStatsConsent
283 DaemonControllerDelegateLinux::GetUsageStatsConsent() {
284 // Crash dump collection is not implemented on Linux yet.
285 // http://crbug.com/130678.
286 DaemonController::UsageStatsConsent consent;
287 consent.supported = false;
288 consent.allowed = false;
289 consent.set_by_policy = false;
290 return consent;
291 }
366 292
367 scoped_ptr<DaemonController> remoting::DaemonController::Create() { 293 scoped_refptr<DaemonController> DaemonController::Create() {
368 return scoped_ptr<DaemonController>(new DaemonControllerLinux()); 294 scoped_ptr<DaemonController::Delegate> delegate(
295 new DaemonControllerDelegateLinux());
296 return new DaemonController(delegate.Pass());
369 } 297 }
370 298
371 } // namespace remoting 299 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698