158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "remoting/host/setup/daemon_controller_delegate_linux.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <unistd.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/base_paths.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/environment.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_writer.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/md5.h"
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/path_service.h"
21a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/kill.h"
22a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/launch.h"
23a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/process_handle.h"
245e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
265e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h"
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/thread_task_runner_handle.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
2946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "build/build_config.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/host/host_config.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/host/json_host_config.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/host/usage_stats_consent.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace remoting {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kDaemonScript[] =
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "/opt/google/chrome-remote-desktop/chrome-remote-desktop";
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// Timeout for running daemon script. The script itself sets a timeout when
433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// waiting for the host to come online, so the setting here should be at least
443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)// as long.
453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)const int64 kDaemonTimeoutMs = 60000;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Timeout for commands that require password prompt - 5 minutes.
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int64 kSudoTimeoutSeconds = 5 * 60;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string GetMd5(const std::string& value) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::MD5Context ctx;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::MD5Init(&ctx);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::MD5Update(&ctx, value);
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::MD5Digest digest;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::MD5Final(&digest, &ctx);
566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return base::StringToLowerASCII(base::HexEncode(digest.a, sizeof(digest.a)));
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)base::FilePath GetConfigPath() {
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string filename = "host#" + GetMd5(net::GetHostName()) + ".json";
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::FilePath homedir;
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  PathService::Get(base::DIR_HOME, &homedir);
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return homedir.Append(".config/chrome-remote-desktop").Append(filename);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool GetScriptPath(base::FilePath* result) {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath candidate_exe(kDaemonScript);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (access(candidate_exe.value().c_str(), X_OK) == 0) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *result = candidate_exe;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool RunHostScriptWithTimeout(
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<std::string>& args,
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::TimeDelta timeout,
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* exit_code) {
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(exit_code);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // As long as we're relying on running an external binary from the
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // PATH, don't do it as root.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (getuid() == 0) {
84424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    LOG(ERROR) << "Refusing to run script as root.";
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath script_path;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetScriptPath(&script_path)) {
89424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    LOG(ERROR) << "GetScriptPath() failed.";
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::CommandLine command_line(script_path);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (unsigned int i = 0; i < args.size(); ++i) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_line.AppendArg(args[i]);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::ProcessHandle process_handle;
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Redirect the child's stdout to the parent's stderr. In the case where this
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // parent process is a Native Messaging host, its stdout is used to send
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // messages to the web-app.
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FileHandleMappingVector fds_to_remap;
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  fds_to_remap.push_back(std::pair<int, int>(STDERR_FILENO, STDOUT_FILENO));
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::LaunchOptions options;
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  options.fds_to_remap = &fds_to_remap;
10546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#if !defined(OS_CHROMEOS)
10746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  options.allow_new_privs = true;
10846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#endif
10946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!base::LaunchProcess(command_line, options, &process_handle)) {
111424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    LOG(ERROR) << "Failed to run command: "
112424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)               << command_line.GetCommandLineString();
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!base::WaitForExitCodeWithTimeout(process_handle, exit_code, timeout)) {
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::KillProcess(process_handle, 0, false);
118424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    LOG(ERROR) << "Timeout exceeded for command: "
119424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)               << command_line.GetCommandLineString();
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool RunHostScript(const std::vector<std::string>& args, int* exit_code) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RunHostScriptWithTimeout(
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      args, base::TimeDelta::FromMilliseconds(kDaemonTimeoutMs), exit_code);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace
13258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)DaemonControllerDelegateLinux::DaemonControllerDelegateLinux() {
13458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)DaemonControllerDelegateLinux::~DaemonControllerDelegateLinux() {
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
13858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)DaemonController::State DaemonControllerDelegateLinux::GetState() {
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::FilePath script_path;
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!GetScriptPath(&script_path)) {
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return DaemonController::STATE_NOT_IMPLEMENTED;
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::CommandLine command_line(script_path);
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  command_line.AppendArg("--get-status");
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string status;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int exit_code = 0;
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool result =
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::GetAppOutputWithExitCode(command_line, &status, &exit_code);
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!result) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(jamiewalch): When we have a good story for installing, return
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // NOT_INSTALLED rather than NOT_IMPLEMENTED (the former suppresses
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the relevant UI in the web-app).
15558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return DaemonController::STATE_NOT_IMPLEMENTED;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (exit_code != 0) {
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               << "\". Exit code: " << exit_code;
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return DaemonController::STATE_UNKNOWN;
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::TrimWhitespaceASCII(status, base::TRIM_ALL, &status);
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (status == "STARTED") {
16758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return DaemonController::STATE_STARTED;
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (status == "STOPPED") {
16958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return DaemonController::STATE_STOPPED;
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (status == "NOT_IMPLEMENTED") {
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return DaemonController::STATE_NOT_IMPLEMENTED;
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Unknown status string returned from  \""
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               << command_line.GetCommandLineString()
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               << "\": " << status;
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return DaemonController::STATE_UNKNOWN;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)scoped_ptr<base::DictionaryValue> DaemonControllerDelegateLinux::GetConfig() {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (GetState() != DaemonController::STATE_NOT_IMPLEMENTED) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    JsonHostConfig config(GetConfigPath());
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (config.Read()) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string value;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (config.GetString(kHostIdConfigPath, &value)) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result->SetString(kHostIdConfigPath, value);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (config.GetString(kXmppLoginConfigPath, &value)) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result->SetString(kXmppLoginConfigPath, value);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      result.reset();  // Return NULL in case of error.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return result.Pass();
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
201a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochvoid DaemonControllerDelegateLinux::InstallHost(
202a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    const DaemonController::CompletionCallback& done) {
203a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  NOTREACHED();
204a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch}
205a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
20658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void DaemonControllerDelegateLinux::SetConfigAndStart(
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> config,
20858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    bool consent,
20958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const DaemonController::CompletionCallback& done) {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add the user to chrome-remote-desktop group first.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> args;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args.push_back("--add-user");
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int exit_code;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!RunHostScriptWithTimeout(
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          args, base::TimeDelta::FromSeconds(kSudoTimeoutSeconds),
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &exit_code) ||
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit_code != 0) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to add user to chrome-remote-desktop group.";
21958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    done.Run(DaemonController::RESULT_FAILED);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ensure the configuration directory exists.
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath config_dir = GetConfigPath().DirName();
2257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!base::DirectoryExists(config_dir) &&
226a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      !base::CreateDirectory(config_dir)) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to create config directory " << config_dir.value();
22858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    done.Run(DaemonController::RESULT_FAILED);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Write config.
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JsonHostConfig config_file(GetConfigPath());
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!config_file.CopyFrom(config.get()) ||
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !config_file.Save()) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to update config file.";
23758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    done.Run(DaemonController::RESULT_FAILED);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finally start the host.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args.clear();
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args.push_back("--start");
24458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
24558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (RunHostScript(args, &exit_code) && (exit_code == 0))
24658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    result = DaemonController::RESULT_OK;
24758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
24858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  done.Run(result);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void DaemonControllerDelegateLinux::UpdateConfig(
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> config,
25358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const DaemonController::CompletionCallback& done) {
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JsonHostConfig config_file(GetConfigPath());
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!config_file.Read() ||
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !config_file.CopyFrom(config.get()) ||
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !config_file.Save()) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to update config file.";
25958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    done.Run(DaemonController::RESULT_FAILED);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> args;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args.push_back("--reload");
26558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  int exit_code = 0;
26658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
26758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (RunHostScript(args, &exit_code) && (exit_code == 0))
26858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    result = DaemonController::RESULT_OK;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  done.Run(result);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void DaemonControllerDelegateLinux::Stop(
27458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const DaemonController::CompletionCallback& done) {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> args;
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args.push_back("--stop");
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int exit_code = 0;
27858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
27958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (RunHostScript(args, &exit_code) && (exit_code == 0))
28058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    result = DaemonController::RESULT_OK;
28158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
28258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  done.Run(result);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void DaemonControllerDelegateLinux::SetWindow(void* window_handle) {
28658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // noop
28758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
28858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
28958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)std::string DaemonControllerDelegateLinux::GetVersion() {
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath script_path;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetScriptPath(&script_path)) {
29258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return std::string();
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::CommandLine command_line(script_path);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  command_line.AppendArg("--host-version");
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string version;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int exit_code = 0;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int result =
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::GetAppOutputWithExitCode(command_line, &version, &exit_code);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result || exit_code != 0) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "\". Exit code: " << exit_code;
30458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return std::string();
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::TrimWhitespaceASCII(version, base::TRIM_ALL, &version);
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!base::ContainsOnlyChars(version, "0123456789.")) {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Received invalid host version number: " << version;
31058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return std::string();
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return version;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)DaemonController::UsageStatsConsent
31758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)DaemonControllerDelegateLinux::GetUsageStatsConsent() {
31858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Crash dump collection is not implemented on Linux yet.
31958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // http://crbug.com/130678.
32058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DaemonController::UsageStatsConsent consent;
32158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  consent.supported = false;
32258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  consent.allowed = false;
32358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  consent.set_by_policy = false;
32458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return consent;
32558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)scoped_refptr<DaemonController> DaemonController::Create() {
32858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  scoped_ptr<DaemonController::Delegate> delegate(
32958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      new DaemonControllerDelegateLinux());
33058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return new DaemonController(delegate.Pass());
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace remoting
334