<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/service/service_process.h"

#include <algorithm>

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/singleton.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/service_process_util.h"
#include "chrome/service/cloud_print/cloud_print_proxy.h"
#include "chrome/service/service_ipc_server.h"
#include "chrome/service/service_process_prefs.h"
#include "net/base/network_change_notifier.h"

#if defined(ENABLE_REMOTING)
#include "remoting/base/constants.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/json_host_config.h"
#endif  // defined(ENABLED_REMOTING)

ServiceProcess* g_service_process = NULL;

namespace {

// Delay in millseconds after the last service is disabled before we attempt
// a shutdown.
const int64 kShutdownDelay = 60000;

class ServiceIOThread : public base::Thread {
 public:
  explicit ServiceIOThread(const char* name);
  virtual ~ServiceIOThread();

 protected:
  virtual void CleanUp();

 private:
  DISALLOW_COPY_AND_ASSIGN(ServiceIOThread);
};

ServiceIOThread::ServiceIOThread(const char* name) : base::Thread(name) {}
ServiceIOThread::~ServiceIOThread() {
  // We cannot rely on our base class to stop the thread since we want our
  // CleanUp function to run.
  Stop();
}

void ServiceIOThread::CleanUp() {
  URLFetcher::CancelAll();
}

}  // namespace

ServiceProcess::ServiceProcess()
  : shutdown_event_(true, false),
    main_message_loop_(NULL),
    enabled_services_(0),
    update_available_(false) {
  DCHECK(!g_service_process);
  g_service_process = this;
}

bool ServiceProcess::Initialize(MessageLoop* message_loop,
                                const CommandLine& command_line) {
  main_message_loop_ = message_loop;
  network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  io_thread_.reset(new ServiceIOThread("ServiceProcess_IO"));
  file_thread_.reset(new base::Thread("ServiceProcess_File"));
  if (!io_thread_->StartWithOptions(options) ||
      !file_thread_->StartWithOptions(options)) {
    NOTREACHED();
    Teardown();
    return false;
  }

  // See if we have been suppiled an LSID in the command line. This LSID will
  // override the credentials we use for Cloud Print.
  std::string lsid = command_line.GetSwitchValueASCII(
          switches::kServiceAccountLsid);

  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  FilePath pref_path = user_data_dir.Append(chrome::kServiceStateFileName);
  service_prefs_.reset(
      new ServiceProcessPrefs(pref_path, file_thread_->message_loop_proxy()));
  service_prefs_->ReadPrefs();

  bool remoting_host_enabled = false;

  // For development, we allow forcing the enabling of the host daemon via a
  // commandline flag, regardless of the preference setting.
  //
  // TODO(ajwong): When we've gotten the preference setting workflow more
  // stable, we should remove the command-line flag force-enable.
  service_prefs_->GetBoolean(prefs::kRemotingHostEnabled,
                             &remoting_host_enabled);
  remoting_host_enabled |= command_line.HasSwitch(switches::kEnableRemoting);

#if defined(ENABLE_REMOTING)
  // Check if remoting host is already enabled.
  if (remoting_host_enabled) {
    StartChromotingHost();
  }
#endif

  // Enable Cloud Print if needed. First check the command-line.
  bool cloud_print_proxy_enabled =
      command_line.HasSwitch(switches::kEnableCloudPrintProxy);
  if (!cloud_print_proxy_enabled) {
    // Then check if the cloud print proxy was previously enabled.
    service_prefs_->GetBoolean(prefs::kCloudPrintProxyEnabled,
                               &cloud_print_proxy_enabled);
  }

  if (cloud_print_proxy_enabled) {
    GetCloudPrintProxy()->EnableForUser(lsid);
  }

  VLOG(1) << "Starting Service Process IPC Server";
  ipc_server_.reset(new ServiceIPCServer(GetServiceProcessChannelName()));
  ipc_server_->Init();

  // After the IPC server has started we signal that the service process is
  // ready.
  ServiceProcessState::GetInstance()->SignalReady(
      NewRunnableMethod(this, &ServiceProcess::Shutdown));

  // See if we need to stay running.
  ScheduleShutdownCheck();
  return true;
}

bool ServiceProcess::Teardown() {
  service_prefs_.reset();
  cloud_print_proxy_.reset();

#if defined(ENABLE_REMOTING)
  ShutdownChromotingHost();
#endif

  ipc_server_.reset();
  // Signal this event before shutting down the service process. That way all
  // background threads can cleanup.
  shutdown_event_.Signal();
  io_thread_.reset();
  file_thread_.reset();
  // The NetworkChangeNotifier must be destroyed after all other threads that
  // might use it have been shut down.
  network_change_notifier_.reset();

  ServiceProcessState::GetInstance()->SignalStopped();
  return true;
}

void ServiceProcess::Shutdown() {
  // Quit the main message loop.
  main_message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

bool ServiceProcess::HandleClientDisconnect() {
  // If there are no enabled services or if there is an update available
  // we want to shutdown right away. Else we want to keep listening for
  // new connections.
  if (!enabled_services_ || update_available()) {
    Shutdown();
    return false;
  }
  return true;
}

CloudPrintProxy* ServiceProcess::GetCloudPrintProxy() {
  if (!cloud_print_proxy_.get()) {
    cloud_print_proxy_.reset(new CloudPrintProxy());
    cloud_print_proxy_->Initialize(service_prefs_.get(), this);
  }
  return cloud_print_proxy_.get();
}

void ServiceProcess::OnCloudPrintProxyEnabled() {
  // Save the preference that we have enabled the cloud print proxy.
  service_prefs_->SetBoolean(prefs::kCloudPrintProxyEnabled, true);
  service_prefs_->WritePrefs();
  OnServiceEnabled();
}

void ServiceProcess::OnCloudPrintProxyDisabled() {
  // Save the preference that we have disabled the cloud print proxy.
  service_prefs_->SetBoolean(prefs::kCloudPrintProxyEnabled, false);
  service_prefs_->WritePrefs();
  OnServiceDisabled();
}

void ServiceProcess::OnServiceEnabled() {
  enabled_services_++;
  if (1 == enabled_services_) {
    ServiceProcessState::GetInstance()->AddToAutoRun();
  }
}

void ServiceProcess::OnServiceDisabled() {
  DCHECK_NE(enabled_services_, 0);
  enabled_services_--;
  if (0 == enabled_services_) {
    ServiceProcessState::GetInstance()->RemoveFromAutoRun();
    // We will wait for some time to respond to IPCs before shutting down.
    ScheduleShutdownCheck();
  }
}

void ServiceProcess::ScheduleShutdownCheck() {
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &ServiceProcess::ShutdownIfNeeded),
      kShutdownDelay);
}

void ServiceProcess::ShutdownIfNeeded() {
  if (0 == enabled_services_) {
    if (ipc_server_->is_client_connected()) {
      // If there is a client connected, we need to try again later.
      // Note that there is still a timing window here because a client may
      // decide to connect at this point.
      // TODO(sanjeevr): Fix this timing window.
      ScheduleShutdownCheck();
    } else {
      Shutdown();
    }
  }
}

#if defined(ENABLE_REMOTING)
bool ServiceProcess::EnableChromotingHostWithTokens(
    const std::string& login,
    const std::string& remoting_token,
    const std::string& talk_token) {
  // Save the login info and tokens.
  remoting_login_ = login;
  remoting_token_ = remoting_token;
  talk_token_ = talk_token;

  // Use the remoting directory to register the host.
  if (remoting_directory_.get())
    remoting_directory_->CancelRequest();
  remoting_directory_.reset(new RemotingDirectoryService(this));
  remoting_directory_->AddHost(remoting_token);
  return true;
}

bool ServiceProcess::StartChromotingHost() {
  // We have already started.
  if (chromoting_context_.get())
    return true;

  // Load chromoting config from the disk.
  LoadChromotingConfig();

  // Start the chromoting context first.
  chromoting_context_.reset(new remoting::ChromotingHostContext());
  chromoting_context_->Start();

  // Create a chromoting host object.
  chromoting_host_ = remoting::ChromotingHost::Create(chromoting_context_.get(),
                                                      chromoting_config_);

  // Then start the chromoting host.
  // When ChromotingHost is shutdown because of failure or a request that
  // we made OnChromotingShutdown() is calls.
  chromoting_host_->Start(
      NewRunnableMethod(this, &ServiceProcess::OnChromotingHostShutdown));
  OnServiceEnabled();
  return true;
}

bool ServiceProcess::ShutdownChromotingHost() {
  // Chromoting host doesn't exist so return true.
  if (!chromoting_host_)
    return true;

  // Shutdown the chromoting host asynchronously. This will signal the host to
  // shutdown, we'll actually wait for all threads to stop when we destroy
  // the chromoting context.
  chromoting_host_->Shutdown();
  chromoting_host_ = NULL;
  return true;
}

void ServiceProcess::OnRemotingHostAdded() {
  // Save configuration for chromoting.
  SaveChromotingConfig(remoting_login_,
                       talk_token_,
                       remoting_directory_->host_id(),
                       remoting_directory_->host_name(),
                       remoting_directory_->host_key_pair());
  remoting_directory_.reset();
  remoting_login_ = "";
  remoting_token_ = "";
  talk_token_ = "";

  // Save the preference that we have enabled the remoting host.
  service_prefs_->SetBoolean(prefs::kRemotingHostEnabled, true);

  // Force writing prefs to the disk.
  service_prefs_->WritePrefs();

  // TODO(hclam): If we have a problem we need to send an IPC message back
  // to the client that started this.
  bool ret = StartChromotingHost();
  DCHECK(ret);
}

void ServiceProcess::OnRemotingDirectoryError() {
  remoting_directory_.reset();
  remoting_login_ = "";
  remoting_token_ = "";
  talk_token_ = "";

  // TODO(hclam): If we have a problem we need to send an IPC message back
  // to the client that started this.
}

void ServiceProcess::SaveChromotingConfig(
    const std::string& login,
    const std::string& token,
    const std::string& host_id,
    const std::string& host_name,
    remoting::HostKeyPair* host_key_pair) {
  // First we need to load the config first.
  LoadChromotingConfig();

  // And then do the update.
  chromoting_config_->SetString(remoting::kXmppLoginConfigPath, login);
  chromoting_config_->SetString(remoting::kXmppAuthTokenConfigPath, token);
  chromoting_config_->SetString(remoting::kHostIdConfigPath, host_id);
  chromoting_config_->SetString(remoting::kHostNameConfigPath, host_name);
  chromoting_config_->Save();

  // And then save the key pair.
  host_key_pair->Save(chromoting_config_);
}

void ServiceProcess::LoadChromotingConfig() {
  // TODO(hclam): We really should be doing this on IO thread so we are not
  // blocked on file IOs.
  if (chromoting_config_)
    return;

  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  FilePath chromoting_config_path =
      user_data_dir.Append(FILE_PATH_LITERAL(".ChromotingConfig.json"));
  chromoting_config_ = new remoting::JsonHostConfig(
      chromoting_config_path, file_thread_->message_loop_proxy());
  if (!chromoting_config_->Read())
    VLOG(1) << "Failed to read chromoting config file.";
}

void ServiceProcess::OnChromotingHostShutdown() {
  // TODO(hclam): Implement.
}
#endif

ServiceProcess::~ServiceProcess() {
  Teardown();
  g_service_process = NULL;
}

// Disable refcounting for runnable method because it is really not needed
// when we post tasks on the main message loop.
DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcess);
<commit_msg>Stop the chromoting_context<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/service/service_process.h"

#include <algorithm>

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/singleton.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/service_process_util.h"
#include "chrome/service/cloud_print/cloud_print_proxy.h"
#include "chrome/service/service_ipc_server.h"
#include "chrome/service/service_process_prefs.h"
#include "net/base/network_change_notifier.h"

#if defined(ENABLE_REMOTING)
#include "remoting/base/constants.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/json_host_config.h"
#endif  // defined(ENABLED_REMOTING)

ServiceProcess* g_service_process = NULL;

namespace {

// Delay in millseconds after the last service is disabled before we attempt
// a shutdown.
const int64 kShutdownDelay = 60000;

class ServiceIOThread : public base::Thread {
 public:
  explicit ServiceIOThread(const char* name);
  virtual ~ServiceIOThread();

 protected:
  virtual void CleanUp();

 private:
  DISALLOW_COPY_AND_ASSIGN(ServiceIOThread);
};

ServiceIOThread::ServiceIOThread(const char* name) : base::Thread(name) {}
ServiceIOThread::~ServiceIOThread() {
  // We cannot rely on our base class to stop the thread since we want our
  // CleanUp function to run.
  Stop();
}

void ServiceIOThread::CleanUp() {
  URLFetcher::CancelAll();
}

}  // namespace

ServiceProcess::ServiceProcess()
  : shutdown_event_(true, false),
    main_message_loop_(NULL),
    enabled_services_(0),
    update_available_(false) {
  DCHECK(!g_service_process);
  g_service_process = this;
}

bool ServiceProcess::Initialize(MessageLoop* message_loop,
                                const CommandLine& command_line) {
  main_message_loop_ = message_loop;
  network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  io_thread_.reset(new ServiceIOThread("ServiceProcess_IO"));
  file_thread_.reset(new base::Thread("ServiceProcess_File"));
  if (!io_thread_->StartWithOptions(options) ||
      !file_thread_->StartWithOptions(options)) {
    NOTREACHED();
    Teardown();
    return false;
  }

  // See if we have been suppiled an LSID in the command line. This LSID will
  // override the credentials we use for Cloud Print.
  std::string lsid = command_line.GetSwitchValueASCII(
          switches::kServiceAccountLsid);

  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  FilePath pref_path = user_data_dir.Append(chrome::kServiceStateFileName);
  service_prefs_.reset(
      new ServiceProcessPrefs(pref_path, file_thread_->message_loop_proxy()));
  service_prefs_->ReadPrefs();

  bool remoting_host_enabled = false;

  // For development, we allow forcing the enabling of the host daemon via a
  // commandline flag, regardless of the preference setting.
  //
  // TODO(ajwong): When we've gotten the preference setting workflow more
  // stable, we should remove the command-line flag force-enable.
  service_prefs_->GetBoolean(prefs::kRemotingHostEnabled,
                             &remoting_host_enabled);
  remoting_host_enabled |= command_line.HasSwitch(switches::kEnableRemoting);

#if defined(ENABLE_REMOTING)
  // Check if remoting host is already enabled.
  if (remoting_host_enabled) {
    StartChromotingHost();
  }
#endif

  // Enable Cloud Print if needed. First check the command-line.
  bool cloud_print_proxy_enabled =
      command_line.HasSwitch(switches::kEnableCloudPrintProxy);
  if (!cloud_print_proxy_enabled) {
    // Then check if the cloud print proxy was previously enabled.
    service_prefs_->GetBoolean(prefs::kCloudPrintProxyEnabled,
                               &cloud_print_proxy_enabled);
  }

  if (cloud_print_proxy_enabled) {
    GetCloudPrintProxy()->EnableForUser(lsid);
  }

  VLOG(1) << "Starting Service Process IPC Server";
  ipc_server_.reset(new ServiceIPCServer(GetServiceProcessChannelName()));
  ipc_server_->Init();

  // After the IPC server has started we signal that the service process is
  // ready.
  ServiceProcessState::GetInstance()->SignalReady(
      NewRunnableMethod(this, &ServiceProcess::Shutdown));

  // See if we need to stay running.
  ScheduleShutdownCheck();
  return true;
}

bool ServiceProcess::Teardown() {
  service_prefs_.reset();
  cloud_print_proxy_.reset();

#if defined(ENABLE_REMOTING)
  ShutdownChromotingHost();
#endif

  ipc_server_.reset();
  // Signal this event before shutting down the service process. That way all
  // background threads can cleanup.
  shutdown_event_.Signal();
  io_thread_.reset();
  file_thread_.reset();
  // The NetworkChangeNotifier must be destroyed after all other threads that
  // might use it have been shut down.
  network_change_notifier_.reset();

  ServiceProcessState::GetInstance()->SignalStopped();
  return true;
}

void ServiceProcess::Shutdown() {
  // Quit the main message loop.
  main_message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

bool ServiceProcess::HandleClientDisconnect() {
  // If there are no enabled services or if there is an update available
  // we want to shutdown right away. Else we want to keep listening for
  // new connections.
  if (!enabled_services_ || update_available()) {
    Shutdown();
    return false;
  }
  return true;
}

CloudPrintProxy* ServiceProcess::GetCloudPrintProxy() {
  if (!cloud_print_proxy_.get()) {
    cloud_print_proxy_.reset(new CloudPrintProxy());
    cloud_print_proxy_->Initialize(service_prefs_.get(), this);
  }
  return cloud_print_proxy_.get();
}

void ServiceProcess::OnCloudPrintProxyEnabled() {
  // Save the preference that we have enabled the cloud print proxy.
  service_prefs_->SetBoolean(prefs::kCloudPrintProxyEnabled, true);
  service_prefs_->WritePrefs();
  OnServiceEnabled();
}

void ServiceProcess::OnCloudPrintProxyDisabled() {
  // Save the preference that we have disabled the cloud print proxy.
  service_prefs_->SetBoolean(prefs::kCloudPrintProxyEnabled, false);
  service_prefs_->WritePrefs();
  OnServiceDisabled();
}

void ServiceProcess::OnServiceEnabled() {
  enabled_services_++;
  if (1 == enabled_services_) {
    ServiceProcessState::GetInstance()->AddToAutoRun();
  }
}

void ServiceProcess::OnServiceDisabled() {
  DCHECK_NE(enabled_services_, 0);
  enabled_services_--;
  if (0 == enabled_services_) {
    ServiceProcessState::GetInstance()->RemoveFromAutoRun();
    // We will wait for some time to respond to IPCs before shutting down.
    ScheduleShutdownCheck();
  }
}

void ServiceProcess::ScheduleShutdownCheck() {
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &ServiceProcess::ShutdownIfNeeded),
      kShutdownDelay);
}

void ServiceProcess::ShutdownIfNeeded() {
  if (0 == enabled_services_) {
    if (ipc_server_->is_client_connected()) {
      // If there is a client connected, we need to try again later.
      // Note that there is still a timing window here because a client may
      // decide to connect at this point.
      // TODO(sanjeevr): Fix this timing window.
      ScheduleShutdownCheck();
    } else {
      Shutdown();
    }
  }
}

#if defined(ENABLE_REMOTING)
bool ServiceProcess::EnableChromotingHostWithTokens(
    const std::string& login,
    const std::string& remoting_token,
    const std::string& talk_token) {
  // Save the login info and tokens.
  remoting_login_ = login;
  remoting_token_ = remoting_token;
  talk_token_ = talk_token;

  // Use the remoting directory to register the host.
  if (remoting_directory_.get())
    remoting_directory_->CancelRequest();
  remoting_directory_.reset(new RemotingDirectoryService(this));
  remoting_directory_->AddHost(remoting_token);
  return true;
}

bool ServiceProcess::StartChromotingHost() {
  // We have already started.
  if (chromoting_context_.get())
    return true;

  // Load chromoting config from the disk.
  LoadChromotingConfig();

  // Start the chromoting context first.
  chromoting_context_.reset(new remoting::ChromotingHostContext());
  chromoting_context_->Start();

  // Create a chromoting host object.
  chromoting_host_ = remoting::ChromotingHost::Create(chromoting_context_.get(),
                                                      chromoting_config_);

  // Then start the chromoting host.
  // When ChromotingHost is shutdown because of failure or a request that
  // we made OnChromotingShutdown() is calls.
  chromoting_host_->Start(
      NewRunnableMethod(this, &ServiceProcess::OnChromotingHostShutdown));
  OnServiceEnabled();
  return true;
}

bool ServiceProcess::ShutdownChromotingHost() {
  // Chromoting host doesn't exist so return true.
  if (!chromoting_host_)
    return true;

  // Shutdown the chromoting host asynchronously. This will signal the host to
  // shutdown, we'll actually wait for all threads to stop when we destroy
  // the chromoting context.
  chromoting_host_->Shutdown();
  chromoting_host_ = NULL;

  chromoting_context_->Stop();
  chromoting_context_ .reset();

  return true;
}

void ServiceProcess::OnRemotingHostAdded() {
  // Save configuration for chromoting.
  SaveChromotingConfig(remoting_login_,
                       talk_token_,
                       remoting_directory_->host_id(),
                       remoting_directory_->host_name(),
                       remoting_directory_->host_key_pair());
  remoting_directory_.reset();
  remoting_login_ = "";
  remoting_token_ = "";
  talk_token_ = "";

  // Save the preference that we have enabled the remoting host.
  service_prefs_->SetBoolean(prefs::kRemotingHostEnabled, true);

  // Force writing prefs to the disk.
  service_prefs_->WritePrefs();

  // TODO(hclam): If we have a problem we need to send an IPC message back
  // to the client that started this.
  bool ret = StartChromotingHost();
  DCHECK(ret);
}

void ServiceProcess::OnRemotingDirectoryError() {
  remoting_directory_.reset();
  remoting_login_ = "";
  remoting_token_ = "";
  talk_token_ = "";

  // TODO(hclam): If we have a problem we need to send an IPC message back
  // to the client that started this.
}

void ServiceProcess::SaveChromotingConfig(
    const std::string& login,
    const std::string& token,
    const std::string& host_id,
    const std::string& host_name,
    remoting::HostKeyPair* host_key_pair) {
  // First we need to load the config first.
  LoadChromotingConfig();

  // And then do the update.
  chromoting_config_->SetString(remoting::kXmppLoginConfigPath, login);
  chromoting_config_->SetString(remoting::kXmppAuthTokenConfigPath, token);
  chromoting_config_->SetString(remoting::kHostIdConfigPath, host_id);
  chromoting_config_->SetString(remoting::kHostNameConfigPath, host_name);
  chromoting_config_->Save();

  // And then save the key pair.
  host_key_pair->Save(chromoting_config_);
}

void ServiceProcess::LoadChromotingConfig() {
  // TODO(hclam): We really should be doing this on IO thread so we are not
  // blocked on file IOs.
  if (chromoting_config_)
    return;

  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  FilePath chromoting_config_path =
      user_data_dir.Append(FILE_PATH_LITERAL(".ChromotingConfig.json"));
  chromoting_config_ = new remoting::JsonHostConfig(
      chromoting_config_path, file_thread_->message_loop_proxy());
  if (!chromoting_config_->Read())
    VLOG(1) << "Failed to read chromoting config file.";
}

void ServiceProcess::OnChromotingHostShutdown() {
  // TODO(hclam): Implement.
}
#endif

ServiceProcess::~ServiceProcess() {
  Teardown();
  g_service_process = NULL;
}

// Disable refcounting for runnable method because it is really not needed
// when we post tasks on the main message loop.
DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcess);
<|endoftext|>


<commit_before>#include <dirent.h>
#include <GRT/GRT.h>
#include <iostream>

#include "mfcc.h"
#include "wav-reader.h"

static const char* kFemaleDatasetDir = "./data/fsew0_v1.1/";
static const char* kMaleDatasetDir = "./data/msak0_v1.1/";

bool load_audio_file_from_directory(
    GRT::TimeSeriesClassificationData& data,
    const char* const directory,
    uint32_t label,
    uint32_t count = numeric_limits<uint32_t>::max()) {
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(directory)) != NULL) {
        while ((ent = readdir(dir)) != NULL) {
            char* suffix = strrchr(ent->d_name, '.');
            if (suffix != NULL && 0 == strcmp(suffix, ".wav")) {
                printf("loading from %s\n", ent->d_name);

                std::string path;
                path.append(directory);
                path.append(ent->d_name);
                WavReader reader(path);

                GRT::MatrixDouble training_sample;
                training_sample.push_back(reader.getData());
                training_sample.transpose();
                data.addSample(label, training_sample);

                if (--count == 0) { return true; }
            }
        }
        closedir(dir);
        return false;
    } else {
        perror("Failed to open .");
        exit(-1);
    }
}

int main(int argc, char* argv[]) {
    GRT::GestureRecognitionPipeline pipeline;

    // Sample rate is 16k, FFT window size 512 => 32 ms, hopsize 128 => 8 ms
    GRT::FFT fft(512, 128, 1,  GRT::FFT::HAMMING_WINDOW, true, false);
    GRT::MFCC mfcc(16000, 512 / 2, 300, 8000, 26, 12, 22);
    GRT::GMM gmm(32, true, true, 0.5);

    pipeline.addFeatureExtractionModule(fft);
    pipeline.addFeatureExtractionModule(mfcc);
    pipeline.setClassifier(gmm);

    GRT::TimeSeriesClassificationData data(1);
    load_audio_file_from_directory(data, kFemaleDatasetDir, 1, 30);
    load_audio_file_from_directory(data, kMaleDatasetDir, 2, 30);
    std::cout << data.getStatsAsString() << std::endl;

    pipeline.train(data);
}
<commit_msg>Add testing<commit_after>#include <dirent.h>
#include <GRT/GRT.h>
#include <iostream>
#include <limits>

#include "mfcc.h"
#include "wav-reader.h"

static const char* kFemaleDatasetDir = "./data/fsew0_v1.1/";
static const char* kMaleDatasetDir = "./data/msak0_v1.1/";

bool load_audio_file_from_directory(
    GRT::TimeSeriesClassificationData& data,
    const char* const directory,
    uint32_t label,
    uint32_t count = numeric_limits<uint32_t>::max(),
    uint32_t skip = 0) {
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(directory)) != NULL) {
        while ((ent = readdir(dir)) != NULL) {
            char* suffix = strrchr(ent->d_name, '.');
            if (suffix != NULL && 0 == strcmp(suffix, ".wav")) {
                if (skip > 0) { skip -= 1; continue; }

                printf("loading from %s\n", ent->d_name);

                std::string path;
                path.append(directory);
                path.append(ent->d_name);
                WavReader reader(path);

                GRT::MatrixDouble training_sample;
                training_sample.push_back(reader.getData());
                training_sample.transpose();
                data.addSample(label, training_sample);

                if (--count == 0) { return true; }
            }
        }
        closedir(dir);
        return false;
    } else {
        perror("Failed to open .");
        exit(-1);
    }
}

int main(int argc, char* argv[]) {
    GRT::GestureRecognitionPipeline pipeline;

    // Sample rate is 16k, FFT window size 512 => 32 ms, hopsize 128 => 8 ms
    GRT::FFT fft(512, 128, 1,  GRT::FFT::HAMMING_WINDOW, true, false);
    GRT::MFCC mfcc(16000, 512 / 2, 300, 8000, 26, 5, 22);
    GRT::GMM gmm(5, true, true, 0.5);

    pipeline.addFeatureExtractionModule(fft);
    pipeline.addFeatureExtractionModule(mfcc);
    pipeline.setClassifier(gmm);

    GRT::TimeSeriesClassificationData data(1);
    load_audio_file_from_directory(data, kFemaleDatasetDir, 1, 1);
    load_audio_file_from_directory(data, kMaleDatasetDir, 2, 1);
    std::cout << data.getStatsAsString() << std::endl;

    pipeline.train(data);

    // GRT::TimeSeriesClassificationData test_data(1);
    // load_audio_file_from_directory(test_data, kFemaleDatasetDir, 1, 1, 30);

    // GRT::MatrixDouble test_samples = test_data.getDataAsMatrixDouble();
    // vector<uint32_t> classified_result(2, 0);
    // for (uint32_t i = 0; i < test_samples.getNumRows(); i++) {
    //     classified_result[pipeline.predict(test_samples.getRowVector(i))]++;
    // }

    // for (const auto& i : classified_result) {
    //     std::cout << i << std::endl;
    // }
}
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QHash>
#include <QDebug>
#include <QX11Info>

#include "windowinfo.h"
#include "xtools/xatomcache.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>

class WindowInfo::WindowData
{

public:
    //! Constructs a window data object
    WindowData(Window id) :
            window(id),
            transientFor(0),
            title(),
            types(),
            states(),
            pid(0),
            pixmapSerial(0)
    {
    }

    //! Destructor
    ~WindowData()
    {
    }

    //! The X window ID
    Window window;

    //! The ID of the window this window is transient for
    Window transientFor;

    //! The title of the window
    QString title;

    //! The window types associated with this window
    QList<Atom> types;

    //! The status atoms of this window
    QList<Atom> states;

    int pid;

    int pixmapSerial;
};

//! Storage for the WindowInfo data objects. A central storage enables constructing
//! new WindowInfo objects with shared data.
static QHash<Window, WindowInfo * > windowDatas;

WindowInfo *WindowInfo::windowFor(Qt::HANDLE wid)
{
    if (WindowInfo *wi = windowDatas.value(wid)) {
        return wi;
    } else {
        return new WindowInfo(wid);
    }
}

WindowInfo::WindowInfo(Qt::HANDLE window)
    : d(new WindowData(window))
{
    qDebug() << Q_FUNC_INFO << "Created WindowInfo for " << window;
    updateWindowTitle();
    updateWindowProperties();
    windowDatas[window] = this;
}

WindowInfo::~WindowInfo()
{
    qDebug() << Q_FUNC_INFO << "Destroyed windwo for " << d->window;
    windowDatas.remove(d->window);
}

const QString& WindowInfo::title() const
{
    return d->title;
}

Qt::HANDLE WindowInfo::window() const
{
    return d->window;
}

Qt::HANDLE WindowInfo::transientFor() const
{
    return d->transientFor;
}

QList<Qt::HANDLE> WindowInfo::types() const
{
    return d->types;
}

QList<Qt::HANDLE> WindowInfo::states() const
{
    return d->states;
}

bool operator==(const WindowInfo &wi1, const WindowInfo &wi2)
{
    return wi1.window() == wi2.window();
}

bool WindowInfo::updateWindowTitle()
{
    Display *dpy = QX11Info::display();
    XTextProperty textProperty;
    bool updated = false;
    int result = XGetTextProperty(dpy, d->window, &textProperty, AtomCache::atom("_NET_WM_NAME"));
    if (result == 0) {
        result = XGetWMName(dpy, d->window, &textProperty);
    }

    if (result != 0) {
        d->title = QString::fromUtf8((const char *)textProperty.value);
        XFree(textProperty.value);
        updated = true;
    }
    return updated;
}

void WindowInfo::updateWindowProperties()
{
    d->types = getWindowProperties(d->window, AtomCache::atom("_NET_WM_WINDOW_TYPE"));
    d->states = getWindowProperties(d->window, AtomCache::atom("_NET_WM_STATE"));

    if (!XGetTransientForHint(QX11Info::display(), d->window, &d->transientFor) || d->transientFor == d->window) {
        d->transientFor = 0;
    }
}

int WindowInfo::pid() const
{
    return d->pid;
}

void WindowInfo::setPid(int pid)
{
    d->pid = pid;
}

QList<Qt::HANDLE> WindowInfo::getWindowProperties(Qt::HANDLE winId, Qt::HANDLE propertyAtom, long maxCount)
{
    QList<Atom> properties;
    Atom actualType;
    int actualFormat;
    unsigned long numTypeItems, bytesLeft;
    unsigned char *typeData = NULL;

    Status result = XGetWindowProperty(QX11Info::display(), winId, propertyAtom, 0L, maxCount, False, XA_ATOM, &actualType, &actualFormat, &numTypeItems, &bytesLeft, &typeData);
    if (result == Success) {
        Atom *type = (Atom *) typeData;
        for (unsigned int n = 0; n < numTypeItems; n++) {
            properties.append(type[n]);
        }
        XFree(typeData);
    }
    return properties;
}

Qt::HANDLE WindowInfo::pixmapSerial() const
{
    return d->pixmapSerial;
}

void WindowInfo::setPixmapSerial(Qt::HANDLE pixmapSerial)
{
    d->pixmapSerial = pixmapSerial;
    qDebug() << Q_FUNC_INFO << "Changed pixmap serial on " << d->window << " to " << d->pixmapSerial;
    emit pixmapSerialChanged();
}

bool WindowInfo::handleXEvent(const XEvent &event)
{
    if (event.type == PropertyNotify &&
             (event.xproperty.atom == AtomCache::atom("_NET_WM_WINDOW_TYPE") ||
              event.xproperty.atom == AtomCache::atom("_NET_WM_STATE")))
    {
        emit visibleInSwitcherChanged();
        return true;
    }

    return false;
}

static QVector<Atom> getNetWmState(Display *display, Window window)
{
    QVector<Atom> atomList;

    Atom actualType;
    int actualFormat;
    ulong propertyLength;
    ulong bytesLeft;
    uchar *propertyData = 0;

    // Step 1: Get the size of the list
    bool result = XGetWindowProperty(display, window, AtomCache::atom("_NET_WM_STATE"), 0, 0,
                                     false, XA_ATOM, &actualType,
                                     &actualFormat, &propertyLength,
                                     &bytesLeft, &propertyData);
    if (result != Success || actualType != XA_ATOM || actualFormat != 32)
        return atomList;

    XFree(propertyData);

    // Step 2: Get the actual list
    if (!XGetWindowProperty(display, window, AtomCache::atom("_NET_WM_STATE"), 0,
                            atomList.size(), false, XA_ATOM,
                            &actualType, &actualFormat,
                            &propertyLength, &bytesLeft,
                            &propertyData) == Success) {
        qWarning("Unable to retrieve window properties: %i", (int) window);
        return atomList;
    }

    atomList.resize(propertyLength);

    if (!atomList.isEmpty())
        memcpy(atomList.data(), propertyData,
               atomList.size() * sizeof(Atom));

    XFree(propertyData);
    return atomList;
}

// TODO: ideally we can cache this and make it a simple getter
// updated when properties change.
bool WindowInfo::visibleInSwitcher()
{
    Atom actualType;
    int actualFormat;
    unsigned char *typeData = NULL;
    unsigned long numTypeItems;
    unsigned long bytesLeft;

    bool result = XGetWindowProperty(QX11Info::display(),
                                     d->window,
                                     AtomCache::atom("_NET_WM_WINDOW_TYPE"),
                                     0L, 16L, false,
                                     XA_ATOM,
                                     &actualType,
                                     &actualFormat,
                                     &numTypeItems,
                                     &bytesLeft,
                                     &typeData);

    if (result != Success)
        return false;

    Atom *type = (Atom *)typeData;
    bool includeInWindowList = false;

    // plain Xlib windows do not have a type
    if (numTypeItems == 0)
        includeInWindowList = true;
    for (unsigned int n = 0; n < numTypeItems; n++) {
        if (type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_DESKTOP") ||
            type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_NOTIFICATION") ||
            type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_DOCK") ||
            type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_MENU"))
        {
            includeInWindowList = false;
            break;
        }
        if (type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_NORMAL"))
        {
            includeInWindowList = true;
        }
    }

    XFree(typeData);

    if (includeInWindowList)
    {
        if (getNetWmState(QX11Info::display(), d->window).contains(AtomCache::atom("_NET_WM_STATE_SKIP_TASKBAR")))
        {
            includeInWindowList = false;
        }
    }

    return includeInWindowList;
}
<commit_msg>Fix check for _NET_WM_STATE_SKIP_TASKBAR<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QHash>
#include <QDebug>
#include <QX11Info>

#include "windowinfo.h"
#include "xtools/xatomcache.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>

class WindowInfo::WindowData
{

public:
    //! Constructs a window data object
    WindowData(Window id) :
            window(id),
            transientFor(0),
            title(),
            types(),
            states(),
            pid(0),
            pixmapSerial(0)
    {
    }

    //! Destructor
    ~WindowData()
    {
    }

    //! The X window ID
    Window window;

    //! The ID of the window this window is transient for
    Window transientFor;

    //! The title of the window
    QString title;

    //! The window types associated with this window
    QList<Atom> types;

    //! The status atoms of this window
    QList<Atom> states;

    int pid;

    int pixmapSerial;
};

//! Storage for the WindowInfo data objects. A central storage enables constructing
//! new WindowInfo objects with shared data.
static QHash<Window, WindowInfo * > windowDatas;

WindowInfo *WindowInfo::windowFor(Qt::HANDLE wid)
{
    if (WindowInfo *wi = windowDatas.value(wid)) {
        return wi;
    } else {
        return new WindowInfo(wid);
    }
}

WindowInfo::WindowInfo(Qt::HANDLE window)
    : d(new WindowData(window))
{
    qDebug() << Q_FUNC_INFO << "Created WindowInfo for " << window;
    updateWindowTitle();
    updateWindowProperties();
    windowDatas[window] = this;
}

WindowInfo::~WindowInfo()
{
    qDebug() << Q_FUNC_INFO << "Destroyed windwo for " << d->window;
    windowDatas.remove(d->window);
}

const QString& WindowInfo::title() const
{
    return d->title;
}

Qt::HANDLE WindowInfo::window() const
{
    return d->window;
}

Qt::HANDLE WindowInfo::transientFor() const
{
    return d->transientFor;
}

QList<Qt::HANDLE> WindowInfo::types() const
{
    return d->types;
}

QList<Qt::HANDLE> WindowInfo::states() const
{
    return d->states;
}

bool operator==(const WindowInfo &wi1, const WindowInfo &wi2)
{
    return wi1.window() == wi2.window();
}

bool WindowInfo::updateWindowTitle()
{
    Display *dpy = QX11Info::display();
    XTextProperty textProperty;
    bool updated = false;
    int result = XGetTextProperty(dpy, d->window, &textProperty, AtomCache::atom("_NET_WM_NAME"));
    if (result == 0) {
        result = XGetWMName(dpy, d->window, &textProperty);
    }

    if (result != 0) {
        d->title = QString::fromUtf8((const char *)textProperty.value);
        XFree(textProperty.value);
        updated = true;
    }
    return updated;
}

void WindowInfo::updateWindowProperties()
{
    d->types = getWindowProperties(d->window, AtomCache::atom("_NET_WM_WINDOW_TYPE"));
    d->states = getWindowProperties(d->window, AtomCache::atom("_NET_WM_STATE"));

    if (!XGetTransientForHint(QX11Info::display(), d->window, &d->transientFor) || d->transientFor == d->window) {
        d->transientFor = 0;
    }
}

int WindowInfo::pid() const
{
    return d->pid;
}

void WindowInfo::setPid(int pid)
{
    d->pid = pid;
}

QList<Qt::HANDLE> WindowInfo::getWindowProperties(Qt::HANDLE winId, Qt::HANDLE propertyAtom, long maxCount)
{
    QList<Atom> properties;
    Atom actualType;
    int actualFormat;
    unsigned long numTypeItems, bytesLeft;
    unsigned char *typeData = NULL;

    Status result = XGetWindowProperty(QX11Info::display(), winId, propertyAtom, 0L, maxCount, False, XA_ATOM, &actualType, &actualFormat, &numTypeItems, &bytesLeft, &typeData);
    if (result == Success) {
        Atom *type = (Atom *) typeData;
        for (unsigned int n = 0; n < numTypeItems; n++) {
            properties.append(type[n]);
        }
        XFree(typeData);
    }
    return properties;
}

Qt::HANDLE WindowInfo::pixmapSerial() const
{
    return d->pixmapSerial;
}

void WindowInfo::setPixmapSerial(Qt::HANDLE pixmapSerial)
{
    d->pixmapSerial = pixmapSerial;
    qDebug() << Q_FUNC_INFO << "Changed pixmap serial on " << d->window << " to " << d->pixmapSerial;
    emit pixmapSerialChanged();
}

bool WindowInfo::handleXEvent(const XEvent &event)
{
    if (event.type == PropertyNotify &&
             (event.xproperty.atom == AtomCache::atom("_NET_WM_WINDOW_TYPE") ||
              event.xproperty.atom == AtomCache::atom("_NET_WM_STATE")))
    {
        emit visibleInSwitcherChanged();
        return true;
    }

    return false;
}

static QVector<Atom> getNetWmState(Display *display, Window window)
{
    QVector<Atom> atomList;

    Atom actualType;
    int actualFormat;
    ulong propertyLength;
    ulong bytesLeft;
    uchar *propertyData = 0;

    // Step 1: Get the size of the list
    bool result = XGetWindowProperty(display, window, AtomCache::atom("_NET_WM_STATE"), 0, 0x7fffffff,
                                     false, XA_ATOM, &actualType,
                                     &actualFormat, &propertyLength,
                                     &bytesLeft, &propertyData);
    if (result != Success || actualType != XA_ATOM || actualFormat != 32)
        return atomList;

    XFree(propertyData);
    atomList.resize(propertyLength);

    // Step 2: Get the actual list
    if (!XGetWindowProperty(display, window, AtomCache::atom("_NET_WM_STATE"), 0,
                            atomList.size(), false, XA_ATOM,
                            &actualType, &actualFormat,
                            &propertyLength, &bytesLeft,
                            &propertyData) == Success) {
        qWarning("Unable to retrieve window properties: %i", (int) window);
        return atomList;
    }

    if (!atomList.isEmpty())
        memcpy(atomList.data(), propertyData,
               atomList.size() * sizeof(Atom));

    XFree(propertyData);
    return atomList;
}

// TODO: ideally we can cache this and make it a simple getter
// updated when properties change.
bool WindowInfo::visibleInSwitcher()
{
    Atom actualType;
    int actualFormat;
    unsigned char *typeData = NULL;
    unsigned long numTypeItems;
    unsigned long bytesLeft;

    bool result = XGetWindowProperty(QX11Info::display(),
                                     d->window,
                                     AtomCache::atom("_NET_WM_WINDOW_TYPE"),
                                     0L, 16L, false,
                                     XA_ATOM,
                                     &actualType,
                                     &actualFormat,
                                     &numTypeItems,
                                     &bytesLeft,
                                     &typeData);

    if (result != Success)
        return false;

    Atom *type = (Atom *)typeData;
    bool includeInWindowList = false;

    // plain Xlib windows do not have a type
    if (numTypeItems == 0)
        includeInWindowList = true;
    for (unsigned int n = 0; n < numTypeItems; n++) {
        if (type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_DESKTOP") ||
            type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_NOTIFICATION") ||
            type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_DOCK") ||
            type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_MENU"))
        {
            includeInWindowList = false;
            break;
        }
        if (type[n] == AtomCache::atom("_NET_WM_WINDOW_TYPE_NORMAL"))
        {
            includeInWindowList = true;
        }
    }

    XFree(typeData);

    if (includeInWindowList)
    {
        if (getNetWmState(QX11Info::display(), d->window).contains(AtomCache::atom("_NET_WM_STATE_SKIP_TASKBAR")))
        {
            includeInWindowList = false;
        }
    }

    return includeInWindowList;
}
<|endoftext|>


<commit_before>#include "concurrency/watchable.hpp"

#include "errors.hpp"
#include <boost/bind.hpp>

#include "concurrency/cond_var.hpp"
#include "concurrency/wait_any.hpp"

template <class outer_type, class callable_type>
class subview_watchable_t : public watchable_t<typename boost::result_of<callable_type(outer_type)>::type> {
public:
    subview_watchable_t(const callable_type &l, watchable_t<outer_type> *p) :
        lens(l), parent(p->clone()) { }

    subview_watchable_t *clone() const {
        return new subview_watchable_t(lens, parent.get());
    }

    typename boost::result_of<callable_type(outer_type)>::type get() {
        return lens(parent->get());
    }

    publisher_t<boost::function<void()> > *get_publisher() {
        return parent->get_publisher();
    }

    rwi_lock_assertion_t *get_rwi_lock_assertion() {
        return parent->get_rwi_lock_assertion();
    }

private:
    callable_type lens;
    clone_ptr_t<watchable_t<outer_type> > parent;
};

template<class value_type>
template<class callable_type>
clone_ptr_t<watchable_t<typename boost::result_of<callable_type(value_type)>::type> > watchable_t<value_type>::subview(const callable_type &lens) {
    assert_thread();
    return clone_ptr_t<watchable_t<typename boost::result_of<callable_type(value_type)>::type> >(
        new subview_watchable_t<value_type, callable_type>(lens, this));
}

template<class value_type>
template<class callable_type>
void watchable_t<value_type>::run_until_satisfied(const callable_type &fun, signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) {
    assert_thread();
    clone_ptr_t<watchable_t<value_type> > clone_this(this->clone());
    while (true) {
        cond_t changed;
        typename watchable_t<value_type>::subscription_t subs(boost::bind(&cond_t::pulse_if_not_already_pulsed, &changed));
        {
            typename watchable_t<value_type>::freeze_t freeze(clone_this);
            if (fun(clone_this->get())) {
                return;
            }
            subs.reset(clone_this, &freeze);
        }
        wait_interruptible(&changed, interruptor);
    }
}

template<class a_type, class b_type, class callable_type>
void run_until_satisfied_2(
        const clone_ptr_t<watchable_t<a_type> > &a,
        const clone_ptr_t<watchable_t<b_type> > &b,
        const callable_type &fun,
        signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) {
    a->assert_thread();
    b->assert_thread();
    while (true) {
        cond_t changed;
        typename watchable_t<a_type>::subscription_t a_subs(boost::bind(&cond_t::pulse_if_not_already_pulsed, &changed));
        typename watchable_t<b_type>::subscription_t b_subs(boost::bind(&cond_t::pulse_if_not_already_pulsed, &changed));
        {
            typename watchable_t<a_type>::freeze_t a_freeze(a);
            typename watchable_t<b_type>::freeze_t b_freeze(b);
            if (fun(a->get(), b->get())) {
                return;
            }
            a_subs.reset(a, &a_freeze);
            b_subs.reset(b, &b_freeze);
        }
        wait_interruptible(&changed, interruptor);
    }
}
<commit_msg>Add more ASSERT_FINITE_CORO_WAITING.<commit_after>#include "concurrency/watchable.hpp"

#include "errors.hpp"
#include <boost/bind.hpp>

#include "concurrency/cond_var.hpp"
#include "concurrency/wait_any.hpp"

template <class outer_type, class callable_type>
class subview_watchable_t : public watchable_t<typename boost::result_of<callable_type(outer_type)>::type> {
public:
    subview_watchable_t(const callable_type &l, watchable_t<outer_type> *p) :
        lens(l), parent(p->clone()) { }

    subview_watchable_t *clone() const {
        return new subview_watchable_t(lens, parent.get());
    }

    typename boost::result_of<callable_type(outer_type)>::type get() {
        return lens(parent->get());
    }

    publisher_t<boost::function<void()> > *get_publisher() {
        return parent->get_publisher();
    }

    rwi_lock_assertion_t *get_rwi_lock_assertion() {
        return parent->get_rwi_lock_assertion();
    }

private:
    callable_type lens;
    clone_ptr_t<watchable_t<outer_type> > parent;
};

template<class value_type>
template<class callable_type>
clone_ptr_t<watchable_t<typename boost::result_of<callable_type(value_type)>::type> > watchable_t<value_type>::subview(const callable_type &lens) {
    assert_thread();
    return clone_ptr_t<watchable_t<typename boost::result_of<callable_type(value_type)>::type> >(
        new subview_watchable_t<value_type, callable_type>(lens, this));
}

template<class value_type>
template<class callable_type>
void watchable_t<value_type>::run_until_satisfied(const callable_type &fun, signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) {
    assert_thread();
    clone_ptr_t<watchable_t<value_type> > clone_this(this->clone());
    while (true) {
        cond_t changed;
        typename watchable_t<value_type>::subscription_t subs(boost::bind(&cond_t::pulse_if_not_already_pulsed, &changed));
        {
            typename watchable_t<value_type>::freeze_t freeze(clone_this);
            ASSERT_FINITE_CORO_WAITING;
            if (fun(clone_this->get())) {
                return;
            }
            subs.reset(clone_this, &freeze);
        }
        wait_interruptible(&changed, interruptor);
    }
}

template<class a_type, class b_type, class callable_type>
void run_until_satisfied_2(
        const clone_ptr_t<watchable_t<a_type> > &a,
        const clone_ptr_t<watchable_t<b_type> > &b,
        const callable_type &fun,
        signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) {
    a->assert_thread();
    b->assert_thread();
    while (true) {
        cond_t changed;
        typename watchable_t<a_type>::subscription_t a_subs(boost::bind(&cond_t::pulse_if_not_already_pulsed, &changed));
        typename watchable_t<b_type>::subscription_t b_subs(boost::bind(&cond_t::pulse_if_not_already_pulsed, &changed));
        {
            typename watchable_t<a_type>::freeze_t a_freeze(a);
            typename watchable_t<b_type>::freeze_t b_freeze(b);
            ASSERT_FINITE_CORO_WAITING;
            if (fun(a->get(), b->get())) {
                return;
            }
            a_subs.reset(a, &a_freeze);
            b_subs.reset(b, &b_freeze);
        }
        wait_interruptible(&changed, interruptor);
    }
}
<|endoftext|>


<commit_before>/**
 * @file fileattributefetch.cpp
 * @brief Classes for file attributes fetching
 *
 * (c) 2013-2014 by Mega Limited, Wellsford, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#include "mega/fileattributefetch.h"
#include "mega/megaclient.h"
#include "mega/megaapp.h"

namespace mega {
FileAttributeFetchChannel::FileAttributeFetchChannel()
{
    req.binary = true;
}

FileAttributeFetch::FileAttributeFetch(handle h, fatype t, int c, int ctag)
{
    nodehandle = h;
    type = t;
    fac = c;
    retries = 0;
    tag = ctag;
}

// post pending requests for this cluster to supplied URL
void FileAttributeFetchChannel::dispatch(MegaClient* client, int fac, const char* targeturl)
{
    req.out->clear();

    // dispatch all pending fetches for this channel's cluster
    for (faf_map::iterator it = client->fafs.begin(); it != client->fafs.end(); it++)
    {
        if (it->second->fac == fac)
        {
            // prevent reallocations
            req.out->reserve(client->fafs.size() * sizeof(handle));

            it->second->dispatched = 1;

            req.out->append((char*)&it->first, sizeof(handle));
        }
    }

    completed = 0;

    req.posturl = targeturl;
    req.post(client);
}

// communicate the result of a file attribute fetch to the application and
// remove completed records
void FileAttributeFetchChannel::parse(MegaClient* client, int fac, bool final)
{
#pragma pack(push,1)
    struct FaPos
    {
        handle h;
        uint32_t pos;
    };
#pragma pack(pop)

    // data is structured as (handle.8.le / position.4.le)* attribute data
    // attributes are CBC-encrypted with the file's key

    // we must have received at least one full header to continue
    if (req.in.size() < sizeof(FaPos))
    {
        if (final) client->faf_failed(fac);
        return;
    }

    uint32_t bod = ((FaPos*)req.in.data())->pos;

    if (req.in.size() < bod)
    {
        if (final) client->faf_failed(fac);
        return;
    }

    const char* fadata;
    uint32_t falen, fapos;
    Node* n;
    faf_map::iterator it;

    fadata = req.in.data();

    while (completed < bod - (final ? 0 : sizeof(FaPos))
        && req.in.size() >= (falen = (final ? req.in.size() : ((FaPos*)(req.in.data() + completed))[1].pos)))
    {
        it = client->fafs.find(((FaPos*)(req.in.data() + completed))->h);

        // locate fetch request (could have been deleted by the application in the meantime)
        if (it != client->fafs.end())
        {
            // locate related node (could have been deleted)
            if ((n = client->nodebyhandle(it->second->nodehandle)))
            {
                fapos = ((FaPos*)(req.in.data() + completed))->pos;
                falen -= fapos;

                if (!(falen & (SymmCipher::BLOCKSIZE - 1)))
                {
                    n->key.cbc_decrypt((byte*)fadata + fapos, falen);

                    client->restag = it->second->tag;

                    client->app->fa_complete(n, it->second->type, fadata + fapos, falen);

                    delete it->second;
                    client->fafs.erase(it);
                }
                else
                {
                    return client->faf_failed(fac);
                }
            }
        }
        else
        {
            return client->faf_failed(fac);
        }

        completed += sizeof(FaPos);
    }

    if (final && completed != bod)
    {
        client->faf_failed(fac);
    }
}
} // namespace
<commit_msg>Fix corrupted last file attribute(s) on fast connections (js)<commit_after>/**
 * @file fileattributefetch.cpp
 * @brief Classes for file attributes fetching
 *
 * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#include "mega/fileattributefetch.h"
#include "mega/megaclient.h"
#include "mega/megaapp.h"

namespace mega {
FileAttributeFetchChannel::FileAttributeFetchChannel()
{
    req.binary = true;
}

FileAttributeFetch::FileAttributeFetch(handle h, fatype t, int c, int ctag)
{
    nodehandle = h;
    type = t;
    fac = c;
    retries = 0;
    tag = ctag;
}

// post pending requests for this cluster to supplied URL
void FileAttributeFetchChannel::dispatch(MegaClient* client, int fac, const char* targeturl)
{
    req.out->clear();

    // dispatch all pending fetches for this channel's cluster
    for (faf_map::iterator it = client->fafs.begin(); it != client->fafs.end(); it++)
    {
        if (it->second->fac == fac)
        {
            // prevent reallocations
            req.out->reserve(client->fafs.size() * sizeof(handle));

            it->second->dispatched = 1;

            req.out->append((char*)&it->first, sizeof(handle));
        }
    }

    completed = 0;

    req.posturl = targeturl;
    req.post(client);
}

// communicate the result of a file attribute fetch to the application and
// remove completed records
void FileAttributeFetchChannel::parse(MegaClient* client, int fac, bool final)
{
#pragma pack(push,1)
    struct FaPos
    {
        handle h;
        uint32_t pos;
    };
#pragma pack(pop)

    // data is structured as (handle.8.le / position.4.le)* attribute data
    // attributes are CBC-encrypted with the file's key

    // we must have received at least one full header to continue
    if (req.in.size() < sizeof(FaPos))
    {
        if (final)
        {
            client->faf_failed(fac);
        }

        return;
    }

    uint32_t bod = ((FaPos*)req.in.data())->pos;

    if (req.in.size() < bod)
    {
        if (final)
        {
            client->faf_failed(fac);
        }

        return;
    }

    const char* fadata;
    uint32_t falen, fapos;
    Node* n;
    faf_map::iterator it;

    fadata = req.in.data();

    while (completed < bod - (final ? 0 : sizeof(FaPos))
        && req.in.size() >= (falen = ((completed == bod - sizeof(FaPos)) ? req.in.size() : ((FaPos*)(req.in.data() + completed))[1].pos)))
    {
        it = client->fafs.find(((FaPos*)(req.in.data() + completed))->h);

        // locate fetch request (could have been deleted by the application in the meantime)
        if (it != client->fafs.end())
        {
            // locate related node (could have been deleted)
            if ((n = client->nodebyhandle(it->second->nodehandle)))
            {
                fapos = ((FaPos*)(req.in.data() + completed))->pos;
                falen -= fapos;

                if (!(falen & (SymmCipher::BLOCKSIZE - 1)))
                {
                    n->key.cbc_decrypt((byte*)fadata + fapos, falen);

                    client->restag = it->second->tag;

                    client->app->fa_complete(n, it->second->type, fadata + fapos, falen);

                    delete it->second;
                    client->fafs.erase(it);
                }
                else
                {
                    return client->faf_failed(fac);
                }
            }
        }
        else
        {
            return client->faf_failed(fac);
        }

        completed += sizeof(FaPos);
    }

    if (final && completed != bod)
    {
        client->faf_failed(fac);
    }
}
} // namespace
<|endoftext|>


<commit_before>// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stout/os.hpp>
#include <stout/strings.hpp>

#include <mesos/mesos.hpp>

#include "common/command_utils.hpp"

#include "slave/containerizer/mesos/provisioner/appc/fetcher.hpp"
#include "slave/containerizer/mesos/provisioner/appc/paths.hpp"

#include "uri/schemes/file.hpp"
#include "uri/schemes/http.hpp"

namespace http = process::http;

using std::string;

using process::Failure;
using process::Future;
using process::Owned;
using process::Shared;

namespace mesos {
namespace internal {
namespace slave {
namespace appc {

static const char EXT[] = "aci";
static const char LABEL_VERSION[] = "version";
static const char LABEL_OS[] = "os";
static const char LABEL_ARCH[] = "arch";

static Try<string> getSimpleDiscoveryImagePath(const Image::Appc& appc)
{
  CHECK(!appc.name().empty());

  hashmap<string, string> labels;
  foreach (const mesos::Label& label, appc.labels().labels()) {
    labels[label.key()] = label.value();
  }

  if (!labels.contains(LABEL_VERSION)) {
    labels.insert({LABEL_VERSION, "latest"});
  }

  if (!labels.contains(LABEL_OS)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_OS) + "' is missing");
  }

  if (!labels.contains(LABEL_ARCH)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_ARCH) + "' is missing");
  }

  return strings::format(
      "%s-%s-%s-%s.%s",
      appc.name(),              // Image name.
      labels.at(LABEL_VERSION), // Version label.
      labels.at(LABEL_OS),      // OS label.
      labels.at(LABEL_ARCH),    // ARCH label.
      EXT);                     // Extension.
}

static Try<URI> getUri(const string& prefix, const string& path)
{
  const string rawUrl = prefix + path;

  // TODO(jojy): Add parse URI function in URI namespace.

  // TODO(jojy): Add `Path::seperator` which abstracts the file
  // separator character.
  if (strings::startsWith(rawUrl, "/")) {
    return uri::file(rawUrl);
  }

  Try<http::URL> _url = http::URL::parse(rawUrl);
  if (_url.isError()) {
    return Error(
        "Failed to parse '" + rawUrl + "' as a valid URL: " + _url.error());
  }

  const http::URL& url = _url.get();

  if (url.domain.isNone() && url.ip.isNone()) {
    return Error(
        "Failed to parse host name from image url '" + rawUrl + "'");
  }

  // Get image server port.
  if (url.port.isNone()) {
    return Error(
        "Failed to parse port for image url '" + rawUrl + "'");
  }

  // Get image server host.
  const string host = url.domain.isSome()
    ? url.domain.get()
    : stringify(url.ip.get());

  int port = static_cast<int>(url.port.get());

  if (url.scheme.get() == "http") {
    return uri::http( host, url.path, port);
  }

  if (url.scheme.get() == "https") {
    return uri::https(host, url.path, port);
  }

  // TODO(jojy): Add support for hdfs.

  return Error("Unsupported scheme '" + url.scheme.get() + "'");
}

Try<Owned<Fetcher>> Fetcher::create(
    const Flags& flags,
    const Shared<uri::Fetcher>& fetcher)
{
  const string prefix = flags.appc_simple_discovery_uri_prefix;

  // TODO(jojy): Add support for hdfs.
  if (!strings::startsWith(prefix, "http") &&
      !strings::startsWith(prefix, "https") &&
      !strings::startsWith(prefix, "/")) {
    return Error("Invalid simple discovery uri prefix: " + prefix);
  }

  return new Fetcher(prefix, fetcher);
}

Fetcher::Fetcher(
    const string& _uriPrefix,
    const Shared<uri::Fetcher>& _fetcher)
  : uriPrefix(_uriPrefix),
    fetcher(_fetcher) {}

Future<Nothing> Fetcher::fetch(const Image::Appc& appc, const Path& directory)
{
  // TODO(jojy): Add more discovery implementations. We use simple
  // discovery now but in the future, this will be extended to use
  // multiple discoveries and try sequentially based on priority.

  if (appc.name().empty()) {
    return Failure("Image name cannot be empty");
  }

  Try<string> path = getSimpleDiscoveryImagePath(appc);
  if (path.isError()) {
    return Failure(
        "Failed to get discovery path for image '" +
        appc.name() + "': " + path.error());
  }

  // First construct a URI based on the scheme.
  Try<URI> uri = getUri(uriPrefix, path.get());
  if (uri.isError()) {
    return Failure(
        "Failed to get URI for image discovery path '" +
        path.get() + "': " + uri.error());
  }

  VLOG(1) << "Fetching image from URI '" << uri.get() << "'";

  // NOTE: URI fetcher will fetch the image into 'directory' with file
  // name as URI's basename.
  const Path aciBundle(path::join(
      directory,
      Path(uri->path()).basename()));

  return fetcher->fetch(uri.get(), directory)
    .then([=]() -> Future<Nothing> {
      // Change the extension to ".gz" as gzip utility expects it.
      const Path _aciBundle(aciBundle.string() + ".gz");

      Try<Nothing> rename = os::rename(aciBundle, _aciBundle);
      if (rename.isError()) {
        return Failure(
            "Failed to change extension to 'gz' for bundle '" +
            stringify(aciBundle) + "': " + rename.error());
      }

      return command::decompress(_aciBundle);
    })
    .then([=]() -> Future<string> {
      return command::sha512(aciBundle);
    })
    .then([=](const string& shasum) -> Future<Nothing> {
      const string imagePath(path::join(directory, "sha512-" + shasum));

      Try<Nothing> mkdir = os::mkdir(imagePath);
      if (mkdir.isError()) {
        return Failure(
            "Failed to create directory for untarring image '" +
            appc.name() + "': " + mkdir.error());
      }

      return command::untar(aciBundle, imagePath);
    })
    .then([=]() -> Future<Nothing> {
      // Remove the bundle file if everything goes well.
      Try<Nothing> remove = os::rm(aciBundle);
      if (remove.isError()) {
        return Failure(
            "Failed to remove aci bundle file '" + stringify(aciBundle) +
            "': " + remove.error());
      }

      return Nothing();
    });
}

} // namespace appc {
} // namespace slave {
} // namespace internal {
} // namespace mesos {
<commit_msg>Removed a redundant space from Appc fetcher.<commit_after>// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stout/os.hpp>
#include <stout/strings.hpp>

#include <mesos/mesos.hpp>

#include "common/command_utils.hpp"

#include "slave/containerizer/mesos/provisioner/appc/fetcher.hpp"
#include "slave/containerizer/mesos/provisioner/appc/paths.hpp"

#include "uri/schemes/file.hpp"
#include "uri/schemes/http.hpp"

namespace http = process::http;

using std::string;

using process::Failure;
using process::Future;
using process::Owned;
using process::Shared;

namespace mesos {
namespace internal {
namespace slave {
namespace appc {

static const char EXT[] = "aci";
static const char LABEL_VERSION[] = "version";
static const char LABEL_OS[] = "os";
static const char LABEL_ARCH[] = "arch";

static Try<string> getSimpleDiscoveryImagePath(const Image::Appc& appc)
{
  CHECK(!appc.name().empty());

  hashmap<string, string> labels;
  foreach (const mesos::Label& label, appc.labels().labels()) {
    labels[label.key()] = label.value();
  }

  if (!labels.contains(LABEL_VERSION)) {
    labels.insert({LABEL_VERSION, "latest"});
  }

  if (!labels.contains(LABEL_OS)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_OS) + "' is missing");
  }

  if (!labels.contains(LABEL_ARCH)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_ARCH) + "' is missing");
  }

  return strings::format(
      "%s-%s-%s-%s.%s",
      appc.name(),              // Image name.
      labels.at(LABEL_VERSION), // Version label.
      labels.at(LABEL_OS),      // OS label.
      labels.at(LABEL_ARCH),    // ARCH label.
      EXT);                     // Extension.
}

static Try<URI> getUri(const string& prefix, const string& path)
{
  const string rawUrl = prefix + path;

  // TODO(jojy): Add parse URI function in URI namespace.

  // TODO(jojy): Add `Path::seperator` which abstracts the file
  // separator character.
  if (strings::startsWith(rawUrl, "/")) {
    return uri::file(rawUrl);
  }

  Try<http::URL> _url = http::URL::parse(rawUrl);
  if (_url.isError()) {
    return Error(
        "Failed to parse '" + rawUrl + "' as a valid URL: " + _url.error());
  }

  const http::URL& url = _url.get();

  if (url.domain.isNone() && url.ip.isNone()) {
    return Error(
        "Failed to parse host name from image url '" + rawUrl + "'");
  }

  // Get image server port.
  if (url.port.isNone()) {
    return Error(
        "Failed to parse port for image url '" + rawUrl + "'");
  }

  // Get image server host.
  const string host = url.domain.isSome()
    ? url.domain.get()
    : stringify(url.ip.get());

  int port = static_cast<int>(url.port.get());

  if (url.scheme.get() == "http") {
    return uri::http(host, url.path, port);
  }

  if (url.scheme.get() == "https") {
    return uri::https(host, url.path, port);
  }

  // TODO(jojy): Add support for hdfs.

  return Error("Unsupported scheme '" + url.scheme.get() + "'");
}

Try<Owned<Fetcher>> Fetcher::create(
    const Flags& flags,
    const Shared<uri::Fetcher>& fetcher)
{
  const string prefix = flags.appc_simple_discovery_uri_prefix;

  // TODO(jojy): Add support for hdfs.
  if (!strings::startsWith(prefix, "http") &&
      !strings::startsWith(prefix, "https") &&
      !strings::startsWith(prefix, "/")) {
    return Error("Invalid simple discovery uri prefix: " + prefix);
  }

  return new Fetcher(prefix, fetcher);
}

Fetcher::Fetcher(
    const string& _uriPrefix,
    const Shared<uri::Fetcher>& _fetcher)
  : uriPrefix(_uriPrefix),
    fetcher(_fetcher) {}

Future<Nothing> Fetcher::fetch(const Image::Appc& appc, const Path& directory)
{
  // TODO(jojy): Add more discovery implementations. We use simple
  // discovery now but in the future, this will be extended to use
  // multiple discoveries and try sequentially based on priority.

  if (appc.name().empty()) {
    return Failure("Image name cannot be empty");
  }

  Try<string> path = getSimpleDiscoveryImagePath(appc);
  if (path.isError()) {
    return Failure(
        "Failed to get discovery path for image '" +
        appc.name() + "': " + path.error());
  }

  // First construct a URI based on the scheme.
  Try<URI> uri = getUri(uriPrefix, path.get());
  if (uri.isError()) {
    return Failure(
        "Failed to get URI for image discovery path '" +
        path.get() + "': " + uri.error());
  }

  VLOG(1) << "Fetching image from URI '" << uri.get() << "'";

  // NOTE: URI fetcher will fetch the image into 'directory' with file
  // name as URI's basename.
  const Path aciBundle(path::join(
      directory,
      Path(uri->path()).basename()));

  return fetcher->fetch(uri.get(), directory)
    .then([=]() -> Future<Nothing> {
      // Change the extension to ".gz" as gzip utility expects it.
      const Path _aciBundle(aciBundle.string() + ".gz");

      Try<Nothing> rename = os::rename(aciBundle, _aciBundle);
      if (rename.isError()) {
        return Failure(
            "Failed to change extension to 'gz' for bundle '" +
            stringify(aciBundle) + "': " + rename.error());
      }

      return command::decompress(_aciBundle);
    })
    .then([=]() -> Future<string> {
      return command::sha512(aciBundle);
    })
    .then([=](const string& shasum) -> Future<Nothing> {
      const string imagePath(path::join(directory, "sha512-" + shasum));

      Try<Nothing> mkdir = os::mkdir(imagePath);
      if (mkdir.isError()) {
        return Failure(
            "Failed to create directory for untarring image '" +
            appc.name() + "': " + mkdir.error());
      }

      return command::untar(aciBundle, imagePath);
    })
    .then([=]() -> Future<Nothing> {
      // Remove the bundle file if everything goes well.
      Try<Nothing> remove = os::rm(aciBundle);
      if (remove.isError()) {
        return Failure(
            "Failed to remove aci bundle file '" + stringify(aciBundle) +
            "': " + remove.error());
      }

      return Nothing();
    });
}

} // namespace appc {
} // namespace slave {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>//===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ClangMove.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/YAMLTraits.h"
#include <set>
#include <string>

using namespace clang;
using namespace llvm;

namespace {

std::error_code CreateNewFile(const llvm::Twine &path) {
  int fd = 0;
  if (std::error_code ec =
          llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
    return ec;

  return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
}

cl::OptionCategory ClangMoveCategory("clang-move options");

cl::list<std::string> Names("names", cl::CommaSeparated,
                            cl::desc("The list of the names of classes being "
                                     "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
                            cl::cat(ClangMoveCategory));

cl::opt<std::string>
    OldHeader("old_header",
              cl::desc("The relative/absolute file path of old header."),
              cl::cat(ClangMoveCategory));

cl::opt<std::string>
    OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
          cl::cat(ClangMoveCategory));

cl::opt<std::string>
    NewHeader("new_header",
              cl::desc("The relative/absolute file path of new header."),
              cl::cat(ClangMoveCategory));

cl::opt<std::string>
    NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
          cl::cat(ClangMoveCategory));

cl::opt<bool> OldDependOnNew(
    "old_depend_on_new",
    cl::desc(
        "Whether old header will depend on new header. If true, clang-move will "
        "add #include of new header to old header."),
    cl::init(false), cl::cat(ClangMoveCategory));

cl::opt<bool> NewDependOnOld(
    "new_depend_on_old",
    cl::desc(
        "Whether new header will depend on old header. If true, clang-move will "
        "add #include of old header to new header."),
    cl::init(false), cl::cat(ClangMoveCategory));

cl::opt<std::string>
    Style("style",
          cl::desc("The style name used for reformatting. Default is \"llvm\""),
          cl::init("llvm"), cl::cat(ClangMoveCategory));

cl::opt<bool> Dump("dump_result",
                   cl::desc("Dump results in JSON format to stdout."),
                   cl::cat(ClangMoveCategory));

cl::opt<bool> DumpDecls(
    "dump_decls",
    cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
             "the option is specified, other command options will be ignored. "
             "An empty JSON will be returned if old header isn't specified."),
    cl::cat(ClangMoveCategory));

} // namespace

int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
  // Add "-fparse-all-comments" compile option to make clang parse all comments,
  // otherwise, ordinary comments like "//" and "/*" won't get parsed (This is
  // a bit of hacky).
  std::vector<std::string> ExtraArgs(argv, argv + argc);
  ExtraArgs.insert(ExtraArgs.begin() + 1, "-extra-arg=-fparse-all-comments");
  std::unique_ptr<const char *[]> RawExtraArgs(
      new const char *[ExtraArgs.size()]);
  for (size_t i = 0; i < ExtraArgs.size(); ++i)
    RawExtraArgs[i] = ExtraArgs[i].c_str();
  int Argc = argc + 1;
  tooling::CommonOptionsParser OptionsParser(Argc, RawExtraArgs.get(),
                                             ClangMoveCategory);

  if (OldDependOnNew && NewDependOnOld) {
    llvm::errs() << "Provide either --old_depend_on_new or "
                    "--new_depend_on_old. clang-move doesn't support these two "
                    "options at same time (It will introduce include cycle).\n";
    return 1;
  }

  tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
                                OptionsParser.getSourcePathList());
  move::MoveDefinitionSpec Spec;
  Spec.Names = {Names.begin(), Names.end()};
  Spec.OldHeader = OldHeader;
  Spec.NewHeader = NewHeader;
  Spec.OldCC = OldCC;
  Spec.NewCC = NewCC;
  Spec.OldDependOnNew = OldDependOnNew;
  Spec.NewDependOnOld = NewDependOnOld;

  llvm::SmallString<128> InitialDirectory;
  if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
    llvm::report_fatal_error("Cannot detect current path: " +
                             Twine(EC.message()));

  move::ClangMoveContext Context{Spec, Tool.getReplacements(),
                                 InitialDirectory.str(), Style, DumpDecls};
  move::DeclarationReporter Reporter;
  auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
      &Context, &Reporter);

  int CodeStatus = Tool.run(Factory.get());
  if (CodeStatus)
    return CodeStatus;

  if (DumpDecls) {
    llvm::outs() << "[\n";
    const auto &Declarations = Reporter.getDeclarationList();
    for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
      llvm::outs() << "  {\n";
      llvm::outs() << "    \"DeclarationName\": \"" << I->first << "\",\n";
      llvm::outs() << "    \"DeclarationType\": \"" << I->second << "\"\n";
      llvm::outs() << "  }";
      // Don't print trailing "," at the end of last element.
      if (I != std::prev(E))
        llvm::outs() << ",\n";
    }
    llvm::outs() << "\n]\n";
    return 0;
  }

  if (!NewCC.empty()) {
    std::error_code EC = CreateNewFile(NewCC);
    if (EC) {
      llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
                   << "\n";
      return EC.value();
    }
  }
  if (!NewHeader.empty()) {
    std::error_code EC = CreateNewFile(NewHeader);
    if (EC) {
      llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
                   << "\n";
      return EC.value();
    }
  }

  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
  DiagnosticsEngine Diagnostics(
      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
      &DiagnosticPrinter, false);
  auto &FileMgr = Tool.getFiles();
  SourceManager SM(Diagnostics, FileMgr);
  Rewriter Rewrite(SM, LangOptions());

  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
    llvm::errs() << "Failed applying all replacements.\n";
    return 1;
  }

  if (Dump) {
    std::set<llvm::StringRef> Files;
    for (const auto &it : Tool.getReplacements())
      Files.insert(it.first);
    auto WriteToJson = [&](llvm::raw_ostream &OS) {
      OS << "[\n";
      for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
        OS << "  {\n";
        OS << "    \"FilePath\": \"" << *I << "\",\n";
        const auto *Entry = FileMgr.getFile(*I);
        auto ID = SM.translateFile(Entry);
        std::string Content;
        llvm::raw_string_ostream ContentStream(Content);
        Rewrite.getEditBuffer(ID).write(ContentStream);
        OS << "    \"SourceText\": \""
           << llvm::yaml::escape(ContentStream.str()) << "\"\n";
        OS << "  }";
        if (I != std::prev(E))
          OS << ",\n";
      }
      OS << "\n]\n";
    };
    WriteToJson(llvm::outs());
    return 0;
  }

  return Rewrite.overwriteChangedFiles();
}
<commit_msg>[clang-move] Use appendArgumentsAdjuster for adding extra arguments<commit_after>//===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ClangMove.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/YAMLTraits.h"
#include <set>
#include <string>

using namespace clang;
using namespace llvm;

namespace {

std::error_code CreateNewFile(const llvm::Twine &path) {
  int fd = 0;
  if (std::error_code ec =
          llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
    return ec;

  return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
}

cl::OptionCategory ClangMoveCategory("clang-move options");

cl::list<std::string> Names("names", cl::CommaSeparated,
                            cl::desc("The list of the names of classes being "
                                     "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
                            cl::cat(ClangMoveCategory));

cl::opt<std::string>
    OldHeader("old_header",
              cl::desc("The relative/absolute file path of old header."),
              cl::cat(ClangMoveCategory));

cl::opt<std::string>
    OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
          cl::cat(ClangMoveCategory));

cl::opt<std::string>
    NewHeader("new_header",
              cl::desc("The relative/absolute file path of new header."),
              cl::cat(ClangMoveCategory));

cl::opt<std::string>
    NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
          cl::cat(ClangMoveCategory));

cl::opt<bool>
    OldDependOnNew("old_depend_on_new",
                   cl::desc("Whether old header will depend on new header. If "
                            "true, clang-move will "
                            "add #include of new header to old header."),
                   cl::init(false), cl::cat(ClangMoveCategory));

cl::opt<bool>
    NewDependOnOld("new_depend_on_old",
                   cl::desc("Whether new header will depend on old header. If "
                            "true, clang-move will "
                            "add #include of old header to new header."),
                   cl::init(false), cl::cat(ClangMoveCategory));

cl::opt<std::string>
    Style("style",
          cl::desc("The style name used for reformatting. Default is \"llvm\""),
          cl::init("llvm"), cl::cat(ClangMoveCategory));

cl::opt<bool> Dump("dump_result",
                   cl::desc("Dump results in JSON format to stdout."),
                   cl::cat(ClangMoveCategory));

cl::opt<bool> DumpDecls(
    "dump_decls",
    cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
             "the option is specified, other command options will be ignored. "
             "An empty JSON will be returned if old header isn't specified."),
    cl::cat(ClangMoveCategory));

} // namespace

int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
  tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);

  if (OldDependOnNew && NewDependOnOld) {
    llvm::errs() << "Provide either --old_depend_on_new or "
                    "--new_depend_on_old. clang-move doesn't support these two "
                    "options at same time (It will introduce include cycle).\n";
    return 1;
  }

  tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
                                OptionsParser.getSourcePathList());
  // Add "-fparse-all-comments" compile option to make clang parse all comments.
  Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
      "-fparse-all-comments", ArgumentInsertPosition::BEGIN));
  move::MoveDefinitionSpec Spec;
  Spec.Names = {Names.begin(), Names.end()};
  Spec.OldHeader = OldHeader;
  Spec.NewHeader = NewHeader;
  Spec.OldCC = OldCC;
  Spec.NewCC = NewCC;
  Spec.OldDependOnNew = OldDependOnNew;
  Spec.NewDependOnOld = NewDependOnOld;

  llvm::SmallString<128> InitialDirectory;
  if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
    llvm::report_fatal_error("Cannot detect current path: " +
                             Twine(EC.message()));

  move::ClangMoveContext Context{Spec, Tool.getReplacements(),
                                 InitialDirectory.str(), Style, DumpDecls};
  move::DeclarationReporter Reporter;
  auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
      &Context, &Reporter);

  int CodeStatus = Tool.run(Factory.get());
  if (CodeStatus)
    return CodeStatus;

  if (DumpDecls) {
    llvm::outs() << "[\n";
    const auto &Declarations = Reporter.getDeclarationList();
    for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
      llvm::outs() << "  {\n";
      llvm::outs() << "    \"DeclarationName\": \"" << I->first << "\",\n";
      llvm::outs() << "    \"DeclarationType\": \"" << I->second << "\"\n";
      llvm::outs() << "  }";
      // Don't print trailing "," at the end of last element.
      if (I != std::prev(E))
        llvm::outs() << ",\n";
    }
    llvm::outs() << "\n]\n";
    return 0;
  }

  if (!NewCC.empty()) {
    std::error_code EC = CreateNewFile(NewCC);
    if (EC) {
      llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
                   << "\n";
      return EC.value();
    }
  }
  if (!NewHeader.empty()) {
    std::error_code EC = CreateNewFile(NewHeader);
    if (EC) {
      llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
                   << "\n";
      return EC.value();
    }
  }

  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
  DiagnosticsEngine Diagnostics(
      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
      &DiagnosticPrinter, false);
  auto &FileMgr = Tool.getFiles();
  SourceManager SM(Diagnostics, FileMgr);
  Rewriter Rewrite(SM, LangOptions());

  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
    llvm::errs() << "Failed applying all replacements.\n";
    return 1;
  }

  if (Dump) {
    std::set<llvm::StringRef> Files;
    for (const auto &it : Tool.getReplacements())
      Files.insert(it.first);
    auto WriteToJson = [&](llvm::raw_ostream &OS) {
      OS << "[\n";
      for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
        OS << "  {\n";
        OS << "    \"FilePath\": \"" << *I << "\",\n";
        const auto *Entry = FileMgr.getFile(*I);
        auto ID = SM.translateFile(Entry);
        std::string Content;
        llvm::raw_string_ostream ContentStream(Content);
        Rewrite.getEditBuffer(ID).write(ContentStream);
        OS << "    \"SourceText\": \""
           << llvm::yaml::escape(ContentStream.str()) << "\"\n";
        OS << "  }";
        if (I != std::prev(E))
          OS << ",\n";
      }
      OS << "\n]\n";
    };
    WriteToJson(llvm::outs());
    return 0;
  }

  return Rewrite.overwriteChangedFiles();
}
<|endoftext|>