Skip to content

Commit

Permalink
Implement IPFS service in utility process
Browse files Browse the repository at this point in the history
  • Loading branch information
yrliou committed Jun 30, 2020
1 parent 54abe58 commit b493e5f
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 3 deletions.
26 changes: 23 additions & 3 deletions chromium_src/chrome/utility/services.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "build/build_config.h"
#include "brave/components/ipfs/browser/buildflags/buildflags.h"

#if !defined(OS_ANDROID)
#include "brave/utility/importer/brave_profile_import_impl.h"
#endif

#if BUILDFLAG(IPFS_ENABLED)
#include "brave/components/services/ipfs/ipfs_service_impl.h"
#include "brave/components/services/ipfs/public/mojom/ipfs_service.mojom.h"
#endif

namespace {

Expand All @@ -17,21 +24,34 @@ auto RunBraveProfileImporter(
}
#endif

} // namespace

#if BUILDFLAG(IPFS_ENABLED)
auto RunIpfsService(mojo::PendingReceiver<ipfs::mojom::IpfsService> receiver) {
return std::make_unique<ipfs::IpfsServiceImpl>(std::move(receiver));
}
#endif

} // namespace

#if defined(OS_ANDROID)
#define BRAVE_PROFILE_IMPORTER
#else
#define BRAVE_PROFILE_IMPORTER \
RunBraveProfileImporter,
#endif

#if BUILDFLAG(IPFS_ENABLED)
#define BRAVE_IPFS_SERVICE \
RunIpfsService,
#else
#define BRAVE_IPFS_SERVICE
#endif

#define BRAVE_GET_MAIN_THREAD_SERVICE_FACTORY \
BRAVE_PROFILE_IMPORTER
BRAVE_PROFILE_IMPORTER \
BRAVE_IPFS_SERVICE

#include "../../../../chrome/utility/services.cc"

#undef BRAVE_GET_MAIN_THREAD_SERVICE_FACTORY
#undef BRAVE_PROFILE_IMPORTER
#undef BRAVE_IPFS_SERVICE
12 changes: 12 additions & 0 deletions components/services/ipfs/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
source_set("ipfs") {
sources = [
"ipfs_service_impl.cc",
"ipfs_service_impl.h",
]

deps = [
"//base",
"//brave/components/services/ipfs/public/mojom",
"//mojo/public/cpp/bindings",
]
}
227 changes: 227 additions & 0 deletions components/services/ipfs/ipfs_service_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/components/services/ipfs/ipfs_service_impl.h"

#if defined(OS_LINUX)
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif

#include <utility>

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"

namespace {

#if defined(OS_POSIX)
int pipehack[2];

static void SIGCHLDHandler(int signo) {
int error = errno;
char ch = 0;
(void)write(pipehack[1], &ch, 1);
errno = error;
}

static void SetupPipeHack() {
if (pipe(pipehack) == -1)
VLOG(0) << "pipehack errno:" << errno;

int flags;
for (size_t i = 0; i < 2; ++i) {
if ((flags = fcntl(pipehack[i], F_GETFL)) == -1)
VLOG(0) << "get flags errno:" << errno;
// Nonblock write end on SIGCHLD handler which will notify monitor thread
// by sending one byte to pipe whose read end is blocked and wait for
// SIGCHLD to arrives to avoid busy reading
if (i == 1)
flags |= O_NONBLOCK;
if (fcntl(pipehack[i], F_SETFL, flags) == -1)
VLOG(0) << "set flags errno:" << errno;
if ((flags = fcntl(pipehack[i], F_GETFD)) == -1)
VLOG(0) << "get fd flags errno:" << errno;
flags |= FD_CLOEXEC;
if (fcntl(pipehack[i], F_SETFD, flags) == -1)
VLOG(0) << "set fd flags errno:" << errno;
}

struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = SIGCHLDHandler;
sigaction(SIGCHLD, &action, NULL);
}

static void TearDownPipeHack() {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &action, NULL);
for (size_t i = 0; i < 2; ++i)
close(pipehack[i]);
}
#endif

} // namespace

namespace ipfs {

IpfsServiceImpl::IpfsServiceImpl(
mojo::PendingReceiver<mojom::IpfsService> receiver)
: receiver_(this, std::move(receiver)) {
#if defined(OS_POSIX)
SetupPipeHack();
#endif
}

IpfsServiceImpl::~IpfsServiceImpl() {
if (ipfs_process_.IsValid()) {
ipfs_process_.Terminate(0, true);
#if defined(OS_POSIX)
TearDownPipeHack();
#endif
#if defined(OS_MACOSX)
base::PostTask(
FROM_HERE,
{base::ThreadPool(),
base::MayBlock(),
base::TaskPriority::BEST_EFFORT},
base::BindOnce(
&base::EnsureProcessTerminated, Passed(&ipfs_process_)));
#else
base::EnsureProcessTerminated(std::move(ipfs_process_));
#endif
}
}

void IpfsServiceImpl::Launch(
mojom::IpfsConfigPtr config, LaunchCallback callback) {
base::FilePath data_path = config->data_root_path;
if (!base::DirectoryExists(data_path)) {
DCHECK(base::CreateDirectory(data_path));
}

base::LaunchOptions options;
#if defined(OS_WIN)
options.environment[L"IPFS_PATH"] = data_path.value();
#else
options.environment["IPFS_PATH"] = data_path.value();
#endif
#if defined(OS_LINUX)
options.kill_on_parent_death = true;
#endif
#if defined(OS_WIN)
options.start_hidden = true;
#endif


// Check if IPFS configs are ready, if not, run ipfs init to initialize them.
base::FilePath config_path = config->config_path;
if (!base::PathExists(config_path)) {
// run ipfs init to gen config
base::CommandLine args(config->binary_path);
args.AppendArg("init");
base::Process init_process = base::LaunchProcess(args, options);
if (!init_process.IsValid()) {
std::move(callback).Run(false, init_process.Pid());
return;
}

int exit_code = 0;
if (!init_process.WaitForExit(&exit_code)) {
VLOG(0) << "Failed to wait init process";
init_process.Close();
std::move(callback).Run(false, init_process.Pid());
return;
}
if (exit_code) {
VLOG(0) << "Failed at running init";
std::move(callback).Run(false, init_process.Pid());
return;
}
}

// Launch IPFS daemon.
base::CommandLine args(config->binary_path);
args.AppendArg("daemon");
args.AppendArg("--migrate=true");
ipfs_process_ = base::LaunchProcess(args, options);
bool result = ipfs_process_.IsValid();

if (callback)
std::move(callback).Run(result, ipfs_process_.Pid());

if (!child_monitor_thread_.get()) {
child_monitor_thread_.reset(new base::Thread("child_monitor_thread"));
if (!child_monitor_thread_->Start()) {
NOTREACHED();
}

child_monitor_thread_->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&IpfsServiceImpl::MonitorChild, base::Unretained(this)));
}
}

void IpfsServiceImpl::MonitorChild() {
#if defined(OS_POSIX)
char buf[PIPE_BUF];

while (1) {
if (read(pipehack[0], buf, sizeof(buf)) > 0) {
pid_t pid;
int status;

if ((pid = waitpid(-1, &status, WNOHANG)) != -1) {
if (WIFSIGNALED(status)) {
LOG(ERROR) << "ipfs got terminated by signal " << WTERMSIG(status);
} else if (WCOREDUMP(status)) {
LOG(ERROR) << "ipfs coredumped";
} else if (WIFEXITED(status)) {
LOG(ERROR) << "ipfs exit (" << WEXITSTATUS(status) << ")";
}
ipfs_process_.Close();
if (receiver_.is_bound() && crash_handler_callback_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(crash_handler_callback_),
pid));
}
}
} else {
// pipes closed
break;
}
}
#elif defined(OS_WIN)
WaitForSingleObject(ipfs_process_.Handle(), INFINITE);
if (receiver_.is_bound() && crash_handler_callback_)
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(crash_handler_callback_),
base::GetProcId(ipfs_process_.Handle())));
#else
#error unsupported platforms
#endif
}

void IpfsServiceImpl::Shutdown(ShutdownCallback callback) {
// TODO(jocelyn): implement shutdown function.
std::move(callback).Run(true, 0);
}

void IpfsServiceImpl::SetCrashHandler(SetCrashHandlerCallback callback) {
crash_handler_callback_ = std::move(callback);
}

} // namespace ipfs
41 changes: 41 additions & 0 deletions components/services/ipfs/ipfs_service_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_SERVICES_IPFS_IPFS_SERVICE_IMPL_H_
#define BRAVE_COMPONENTS_SERVICES_IPFS_IPFS_SERVICE_IMPL_H_

#include <memory>

#include "base/process/process.h"
#include "brave/components/services/ipfs/public/mojom/ipfs_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"

namespace ipfs {

class IpfsServiceImpl : public mojom::IpfsService {
public:
explicit IpfsServiceImpl(mojo::PendingReceiver<mojom::IpfsService> receiver);
~IpfsServiceImpl() override;

private:
// mojom::IpfsService
void Launch(mojom::IpfsConfigPtr config,
LaunchCallback callback) override;
void Shutdown(ShutdownCallback callback) override;
void SetCrashHandler(SetCrashHandlerCallback callback) override;
void MonitorChild();

base::Process ipfs_process_;
mojo::Receiver<mojom::IpfsService> receiver_;
SetCrashHandlerCallback crash_handler_callback_;
std::unique_ptr<base::Thread> child_monitor_thread_;

DISALLOW_COPY_AND_ASSIGN(IpfsServiceImpl);
};

} // namespace ipfs

#endif // BRAVE_COMPONENTS_SERVICES_IPFS_IPFS_SERVICE_IMPL_H_
10 changes: 10 additions & 0 deletions components/services/ipfs/public/mojom/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import("//mojo/public/tools/bindings/mojom.gni")

mojom("mojom") {
sources = [
"ipfs_service.mojom",
]
public_deps = [
"//mojo/public/mojom/base",
]
}
19 changes: 19 additions & 0 deletions components/services/ipfs/public/mojom/ipfs_service.mojom
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2020 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
module ipfs.mojom;

import "mojo/public/mojom/base/file_path.mojom";

struct IpfsConfig {
mojo_base.mojom.FilePath binary_path;
mojo_base.mojom.FilePath config_path;
mojo_base.mojom.FilePath data_root_path;
};

interface IpfsService {
Launch(ipfs.mojom.IpfsConfig config) => (bool result, int64 pid);
Shutdown() => (bool result, int64 pid);
SetCrashHandler() => (int64 pid);
};
5 changes: 5 additions & 0 deletions utility/BUILD.gn
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import("//brave/browser/tor/buildflags/buildflags.gni")
import("//brave/components/brave_ads/browser/buildflags/buildflags.gni")
import("//brave/components/brave_rewards/browser/buildflags/buildflags.gni")
import("//brave/components/ipfs/browser/buildflags/buildflags.gni")
import("//build/config/features.gni")
import("//build/config/ui.gni")

Expand Down Expand Up @@ -59,6 +60,10 @@ source_set("utility") {
deps += [ "//brave/components/services/tor" ]
}

if (ipfs_enabled) {
deps += [ "//brave/components/services/ipfs" ]
}

if (brave_ads_enabled) {
deps += [ "//brave/components/services/bat_ads:lib" ]
}
Expand Down

0 comments on commit b493e5f

Please sign in to comment.