blob: 19c43d65094fb4a0227efccf5e2ec92b855fe113 [file] [log] [blame]
// Copyright 2014 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 "services/service_manager/service_manager.h"
#include <utility>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/token.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "sandbox/policy/mojom/sandbox.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/constants.h"
#include "services/service_manager/public/cpp/manifest_builder.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/mojom/connector.mojom.h"
#include "services/service_manager/public/mojom/service.mojom.h"
#include "services/service_manager/public/mojom/service_control.mojom.h"
#include "services/service_manager/public/mojom/service_manager.mojom.h"
#include "services/service_manager/service_instance.h"
#include "services/service_manager/service_process_host.h"
#if !BUILDFLAG(IS_IOS)
#include "services/service_manager/service_process_launcher.h"
#endif
namespace service_manager {
namespace {
const char kCapability_ServiceManager[] = "service_manager:service_manager";
#if BUILDFLAG(IS_WIN)
const char kServiceExecutableExtension[] = ".service.exe";
#elif !BUILDFLAG(IS_IOS)
const char kServiceExecutableExtension[] = ".service";
#endif
base::ProcessId GetCurrentPid() {
#if BUILDFLAG(IS_IOS)
// iOS does not support base::Process.
return 0;
#else
return base::Process::Current().Pid();
#endif
}
const Identity& GetServiceManagerInstanceIdentity() {
static base::NoDestructor<Identity> id{service_manager::mojom::kServiceName,
kSystemInstanceGroup, base::Token{},
base::Token::CreateRandom()};
return *id;
}
// Default ServiceProcessHost implementation. This launches a service process
// from a standalone executable only.
class DefaultServiceProcessHost : public ServiceProcessHost {
public:
explicit DefaultServiceProcessHost(const base::FilePath& executable_path)
#if !BUILDFLAG(IS_IOS)
: launcher_(nullptr, executable_path)
#endif
{
}
DefaultServiceProcessHost(const DefaultServiceProcessHost&) = delete;
DefaultServiceProcessHost& operator=(const DefaultServiceProcessHost&) =
delete;
~DefaultServiceProcessHost() override = default;
mojo::PendingRemote<mojom::Service> Launch(
const Identity& identity,
sandbox::mojom::Sandbox sandbox_type,
const std::u16string& display_name,
LaunchCallback callback) override {
#if BUILDFLAG(IS_IOS)
return mojo::NullRemote();
#else
// TODO(https://crbug.com/781334): Support sandboxing.
CHECK_EQ(sandbox_type, sandbox::mojom::Sandbox::kNoSandbox);
return launcher_.Start(identity, sandbox::mojom::Sandbox::kNoSandbox,
std::move(callback));
#endif // BUILDFLAG(IS_IOS)
}
private:
#if !BUILDFLAG(IS_IOS)
ServiceProcessLauncher launcher_;
#endif
};
// Default ServiceManager::Delegate implementation. This supports launching only
// standalone service executables.
//
// TODO(https://crbug.com/781334): Migrate all service process support into this
// implementation and merge it into ServiceProcessHost.
class DefaultServiceManagerDelegate : public ServiceManager::Delegate {
public:
explicit DefaultServiceManagerDelegate(
ServiceManager::ServiceExecutablePolicy service_executable_policy)
: service_executable_policy_(service_executable_policy) {}
DefaultServiceManagerDelegate(const DefaultServiceManagerDelegate&) = delete;
DefaultServiceManagerDelegate& operator=(
const DefaultServiceManagerDelegate&) = delete;
~DefaultServiceManagerDelegate() override = default;
bool RunBuiltinServiceInstanceInCurrentProcess(
const Identity& identity,
mojo::PendingReceiver<mojom::Service> receiver) override {
return false;
}
std::unique_ptr<ServiceProcessHost>
CreateProcessHostForBuiltinServiceInstance(
const Identity& identity) override {
return nullptr;
}
std::unique_ptr<ServiceProcessHost> CreateProcessHostForServiceExecutable(
const base::FilePath& executable_path) override {
if (service_executable_policy_ ==
ServiceManager::ServiceExecutablePolicy::kNotSupported) {
return nullptr;
}
DCHECK_EQ(ServiceManager::ServiceExecutablePolicy::kSupported,
service_executable_policy_);
return std::make_unique<DefaultServiceProcessHost>(executable_path);
}
private:
const ServiceManager::ServiceExecutablePolicy service_executable_policy_;
};
} // namespace
ServiceManager::ServiceManager(const std::vector<Manifest>& manifests,
std::unique_ptr<Delegate> delegate)
: delegate_(std::move(delegate)), catalog_(manifests) {
Manifest service_manager_manifest =
ManifestBuilder()
.WithOptions(ManifestOptionsBuilder()
.WithInstanceSharingPolicy(
Manifest::InstanceSharingPolicy::kSingleton)
.Build())
.ExposeCapability(kCapability_ServiceManager,
Manifest::InterfaceList<mojom::ServiceManager>())
.Build();
service_manager_instance_ = CreateServiceInstance(
GetServiceManagerInstanceIdentity(), service_manager_manifest);
service_manager_instance_->SetPID(GetCurrentPid());
mojo::PendingRemote<mojom::Service> remote;
service_receiver_.Bind(remote.InitWithNewPipeAndPassReceiver());
service_manager_instance_->StartWithRemote(std::move(remote));
}
ServiceManager::ServiceManager(
const std::vector<Manifest>& manifests,
ServiceExecutablePolicy service_executable_policy)
: ServiceManager(manifests,
std::make_unique<DefaultServiceManagerDelegate>(
service_executable_policy)) {}
ServiceManager::~ServiceManager() {
// Stop all of the instances before destroying any of them. This ensures that
// all Services will receive connection errors and avoids possible deadlock in
// the case where one ServiceInstance's destructor blocks waiting for its
// Runner to quit, while that Runner's corresponding Service blocks its
// shutdown on a distinct Service receiving a connection error.
//
// Also ensure we tear down the ServiceManager instance last. This is to avoid
// hitting bindings DCHECKs, since the ServiceManager or Catalog may at any
// given time own in-flight responders for Instances' Connector requests.
for (auto& instance : instances_) {
if (instance.get() != service_manager_instance_)
instance->Stop();
}
service_manager_instance_->Stop();
instances_.clear();
}
ServiceInstance* ServiceManager::FindOrCreateMatchingTargetInstance(
const ServiceInstance& source_instance,
const ServiceFilter& partial_target_filter) {
TRACE_EVENT_INSTANT1("service_manager", "ServiceManager::Connect",
TRACE_EVENT_SCOPE_THREAD, "original_name",
partial_target_filter.service_name());
if (partial_target_filter.service_name() == mojom::kServiceName)
return service_manager_instance_;
const service_manager::Manifest* manifest =
catalog_.GetManifest(partial_target_filter.service_name());
if (!manifest) {
LOG(ERROR) << "Failed to resolve service name: "
<< partial_target_filter.service_name();
return nullptr;
}
service_manager::ServiceFilter target_filter = partial_target_filter;
if (!target_filter.instance_group()) {
// Inherit the source instance's group if none was specified by the
// caller's provided filter.
target_filter.set_instance_group(
source_instance.identity().instance_group());
}
if (!target_filter.instance_id()) {
// Assume the default (zero) instance ID if none was specified by the
// caller's provided filter.
target_filter.set_instance_id(base::Token());
}
// Use an existing instance if possible.
ServiceInstance* target_instance =
instance_registry_.FindMatching(target_filter);
if (target_instance)
return target_instance;
// If there was no existing instance but the caller is requesting a specific
// globally unique ID for the target, ignore the request. That instance is
// obviously no longer running, and globally unique IDs are never reused.
if (target_filter.globally_unique_id())
return nullptr;
const service_manager::Manifest* parent_manifest =
catalog_.GetParentManifest(manifest->service_name);
// New instances to be shared globally or across instance groups are assigned
// their own random instance group. Packaged service instances also retain
// the target filter group regardless of sharing policy.
const base::Token target_group =
manifest->options.instance_sharing_policy ==
Manifest::InstanceSharingPolicy::kNoSharing ||
parent_manifest
? *target_filter.instance_group()
: base::Token::CreateRandom();
// New singleton instances are always forced to instance ID zero.
const base::Token target_instance_id =
manifest->options.instance_sharing_policy ==
Manifest::InstanceSharingPolicy::kSingleton
? base::Token()
: *target_filter.instance_id();
DCHECK(!target_instance);
target_instance = CreateServiceInstance(
Identity(target_filter.service_name(), target_group, target_instance_id,
target_filter.globally_unique_id().value_or(
base::Token::CreateRandom())),
*manifest);
if (parent_manifest) {
// This service is provided by another service, as indicated by the
// providing service's manifest. Get an instance of that service first, and
// ask it to create an instance of this one.
auto factory_filter = ServiceFilter::ByNameWithIdInGroup(
parent_manifest->service_name, *target_filter.instance_id(),
*target_filter.instance_group());
ServiceInstance* factory_instance = FindOrCreateMatchingTargetInstance(
*service_manager_instance_, factory_filter);
mojo::PendingRemote<mojom::ProcessMetadata> metadata;
auto metadata_receiver = metadata.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<mojom::Service> remote;
bool created =
factory_instance &&
factory_instance->CreatePackagedServiceInstance(
target_instance->identity(),
remote.InitWithNewPipeAndPassReceiver(), std::move(metadata));
if (!created) {
DestroyInstance(target_instance);
return nullptr;
}
target_instance->BindProcessMetadataReceiver(std::move(metadata_receiver));
target_instance->StartWithRemote(std::move(remote));
return target_instance;
}
switch (manifest->options.execution_mode) {
case Manifest::ExecutionMode::kInProcessBuiltin: {
mojo::PendingRemote<mojom::Service> remote;
if (!delegate_->RunBuiltinServiceInstanceInCurrentProcess(
target_instance->identity(),
remote.InitWithNewPipeAndPassReceiver())) {
DestroyInstance(target_instance);
return nullptr;
}
target_instance->StartWithRemote(std::move(remote));
break;
}
#if !BUILDFLAG(IS_IOS)
case Manifest::ExecutionMode::kOutOfProcessBuiltin: {
auto process_host = delegate_->CreateProcessHostForBuiltinServiceInstance(
target_instance->identity());
if (!process_host || !target_instance->StartWithProcessHost(
std::move(process_host),
sandbox::policy::UtilitySandboxTypeFromString(
manifest->options.sandbox_type))) {
DestroyInstance(target_instance);
return nullptr;
}
break;
}
case Manifest::ExecutionMode::kStandaloneExecutable: {
base::FilePath service_exe_root;
CHECK(base::PathService::Get(base::DIR_ASSETS, &service_exe_root));
auto process_host = delegate_->CreateProcessHostForServiceExecutable(
service_exe_root.AppendASCII(manifest->service_name +
kServiceExecutableExtension));
if (!process_host || !target_instance->StartWithProcessHost(
std::move(process_host),
sandbox::policy::UtilitySandboxTypeFromString(
manifest->options.sandbox_type))) {
DestroyInstance(target_instance);
return nullptr;
}
break;
}
#else // !BUILDFLAG(IS_IOS)
default:
NOTREACHED();
return nullptr;
#endif // !BUILDFLAG(IS_IOS)
}
return target_instance;
}
void ServiceManager::StartService(const std::string& service_name) {
FindOrCreateMatchingTargetInstance(
*service_manager_instance_,
ServiceFilter::ByNameInGroup(service_name, kSystemInstanceGroup));
}
bool ServiceManager::QueryCatalog(const std::string& service_name,
const base::Token& instance_group,
std::string* sandbox_type) {
const Manifest* manifest = catalog_.GetManifest(service_name);
if (!manifest)
return false;
*sandbox_type = manifest->options.sandbox_type;
return true;
}
bool ServiceManager::RegisterService(
const Identity& identity,
mojo::PendingRemote<mojom::Service> service,
mojo::PendingReceiver<mojom::ProcessMetadata> metadata_receiver) {
if (!identity.IsValid())
return false;
const service_manager::Manifest* manifest =
catalog_.GetManifest(identity.name());
if (!manifest) {
LOG(ERROR) << "Failed to resolve service name: " << identity.name();
return false;
}
ServiceInstance* instance = CreateServiceInstance(identity, *manifest);
if (metadata_receiver)
instance->BindProcessMetadataReceiver(std::move(metadata_receiver));
else
instance->SetPID(GetCurrentPid());
instance->StartWithRemote(std::move(service));
return true;
}
void ServiceManager::MakeInstanceUnreachable(ServiceInstance* instance) {
instance_registry_.Unregister(instance);
}
void ServiceManager::DestroyInstance(ServiceInstance* instance) {
// We never clean up the ServiceManager's own instance.
if (instance == service_manager_instance_)
return;
MakeInstanceUnreachable(instance);
auto it = instances_.find(instance);
DCHECK(it != instances_.end());
// Deletes |instance|.
instances_.erase(it);
}
void ServiceManager::OnInstanceStopped(const Identity& identity) {
for (auto& listener : listeners_) {
listener->OnServiceStopped(identity);
}
}
ServiceInstance* ServiceManager::GetExistingInstance(
const Identity& identity) const {
return instance_registry_.FindMatching(
ServiceFilter::ForExactIdentity(identity));
}
void ServiceManager::NotifyServiceCreated(const ServiceInstance& instance) {
mojom::RunningServiceInfoPtr info = instance.CreateRunningServiceInfo();
for (auto& listener : listeners_) {
listener->OnServiceCreated(info.Clone());
}
}
void ServiceManager::NotifyServiceStarted(const Identity& identity,
base::ProcessId pid) {
for (auto& listener : listeners_) {
listener->OnServiceStarted(identity, pid);
}
}
void ServiceManager::NotifyServiceFailedToStart(const Identity& identity) {
for (auto& listener : listeners_) {
listener->OnServiceFailedToStart(identity);
}
}
void ServiceManager::NotifyServicePIDReceived(const Identity& identity,
base::ProcessId pid) {
for (auto& listener : listeners_) {
listener->OnServicePIDReceived(identity, pid);
}
}
ServiceInstance* ServiceManager::CreateServiceInstance(
const Identity& identity,
const Manifest& manifest) {
DCHECK(identity.IsValid());
auto instance = std::make_unique<ServiceInstance>(this, identity, manifest);
auto* raw_instance = instance.get();
instances_.insert(std::move(instance));
// NOTE: |instance| has been passed elsewhere. Use |raw_instance| from this
// point forward. It's safe for the extent of this method.
instance_registry_.Register(raw_instance);
return raw_instance;
}
void ServiceManager::AddListener(
mojo::PendingRemote<mojom::ServiceManagerListener> listener) {
std::vector<mojom::RunningServiceInfoPtr> infos;
for (auto& instance : instances_)
infos.push_back(instance->CreateRunningServiceInfo());
mojo::Remote<mojom::ServiceManagerListener> listener_remote;
listener_remote.Bind(std::move(listener));
listener_remote->OnInit(std::move(infos));
listeners_.Add(std::move(listener_remote));
}
void ServiceManager::OnBindInterface(
const BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle receiving_pipe) {
ServiceInstance* instance = GetExistingInstance(source_info.identity);
DCHECK(instance);
if (interface_name == mojom::ServiceManager::Name_) {
instance->BindServiceManagerReceiver(
mojo::PendingReceiver<mojom::ServiceManager>(
std::move(receiving_pipe)));
}
}
} // namespace service_manager