blob: ca21845c7a80c3233032b15ec1691c58d48e7415 [file] [log] [blame]
// Copyright 2018 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 "webrunner/service/context_provider_impl.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async/default.h>
#include <lib/fdio/io.h>
#include <lib/fdio/util.h>
#include <lib/zx/job.h>
#include <stdio.h>
#include <zircon/processargs.h>
#include <utility>
#include "base/base_paths_fuchsia.h"
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/fuchsia/default_job.h"
#include "base/fuchsia/file_utils.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "webrunner/service/common.h"
namespace webrunner {
namespace {
// Relaunches the current executable as a Context process.
base::Process LaunchContextProcess(const base::CommandLine& launch_command,
const base::LaunchOptions& launch_options) {
return base::LaunchProcess(launch_command, launch_options);
}
// Returns true if |handle| is connected to a directory.
bool IsValidDirectory(zx_handle_t handle) {
base::File directory =
base::fuchsia::GetFileFromHandle(zx::handle(fdio_service_clone(handle)));
if (!directory.IsValid())
return false;
base::File::Info info;
if (!directory.GetInfo(&info)) {
LOG(ERROR) << "Could not query FileInfo for handle.";
directory.Close();
return false;
}
if (!info.is_directory) {
LOG(ERROR) << "Handle is not a directory.";
return false;
}
return true;
}
} // namespace
ContextProviderImpl::ContextProviderImpl() : ContextProviderImpl(false) {}
ContextProviderImpl::ContextProviderImpl(bool use_shared_tmp)
: launch_(base::BindRepeating(&LaunchContextProcess)),
use_shared_tmp_(use_shared_tmp) {}
ContextProviderImpl::~ContextProviderImpl() = default;
// static
std::unique_ptr<ContextProviderImpl> ContextProviderImpl::CreateForTest() {
// Bind the unique_ptr in a two step process.
// std::make_unique<> doesn't work well with private constructors,
// and the unique_ptr(raw_ptr*) constructor format isn't permitted as per
// PRESUBMIT.py policy.
std::unique_ptr<ContextProviderImpl> provider;
provider.reset(new ContextProviderImpl(true));
return provider;
}
void ContextProviderImpl::SetLaunchCallbackForTests(
const LaunchContextProcessCallback& launch) {
launch_ = launch;
}
void ContextProviderImpl::Create(
chromium::web::CreateContextParams params,
::fidl::InterfaceRequest<chromium::web::Context> context_request) {
DCHECK(context_request.is_valid());
base::CommandLine launch_command = *base::CommandLine::ForCurrentProcess();
base::LaunchOptions launch_options;
// Clone job because the context needs to be able to spawn child processes.
launch_options.spawn_flags = FDIO_SPAWN_CLONE_JOB;
// Clone stdout/stderr to get logs in system debug log.
launch_options.fds_to_remap.push_back(
std::make_pair(STDERR_FILENO, STDERR_FILENO));
launch_options.fds_to_remap.push_back(
std::make_pair(STDOUT_FILENO, STDOUT_FILENO));
// Context and child processes need access to the read-only package files.
launch_options.paths_to_clone.push_back(base::FilePath("/pkg"));
// Context needs access to the read-only SSL root certificates list.
launch_options.paths_to_clone.push_back(base::FilePath("/config/ssl"));
if (use_shared_tmp_)
launch_options.paths_to_clone.push_back(base::FilePath("/tmp"));
// Transfer the ContextRequest handle to a well-known location in the child
// process' handle table.
zx::channel context_handle(context_request.TakeChannel());
launch_options.handles_to_transfer.push_back(
{kContextRequestHandleId, context_handle.get()});
// Pass /svc directory specified by the caller.
launch_options.paths_to_transfer.push_back(base::PathToTransfer{
base::FilePath("/svc"), std::move(params.service_directory.release())});
// Bind |data_directory| to /data directory, if provided.
if (params.data_directory) {
if (!IsValidDirectory(params.data_directory.get()))
return;
base::FilePath data_path;
CHECK(base::PathService::Get(base::DIR_APP_DATA, &data_path));
launch_options.paths_to_transfer.push_back(
base::PathToTransfer{data_path, params.data_directory.release()});
}
// Isolate the child Context processes by containing them within their own
// respective jobs.
zx::job job;
zx_status_t status = zx::job::create(*base::GetDefaultJob(), 0, &job);
ZX_CHECK(status == ZX_OK, status) << "zx_job_create";
launch_options.job_handle = job.get();
ignore_result(launch_.Run(std::move(launch_command), launch_options));
ignore_result(context_handle.release());
ignore_result(job.release());
}
void ContextProviderImpl::Bind(
fidl::InterfaceRequest<chromium::web::ContextProvider> request) {
bindings_.AddBinding(this, std::move(request));
}
} // namespace webrunner