blob: 4056fcb8828140be5f3fed48e4b91333a69f7f31 [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 <lib/fdio/namespace.h>
#include <lib/sys/cpp/testing/test_with_environment_fixture.h>
#include <lib/zx/channel.h>
#include <lib/zx/process.h>
#include <zircon/process.h>
#include <zircon/rights.h>
#include <zircon/syscalls/object.h>
#include "src/lib/fxl/memory/weak_ptr.h"
#include "src/sys/appmgr/realm.h"
using fuchsia::sys::ServiceList;
using fuchsia::sys::ServiceListPtr;
namespace component {
namespace {
class NamespaceGuard {
public:
explicit NamespaceGuard(fxl::RefPtr<Namespace> ns) : ns_(std::move(ns)) {}
NamespaceGuard(std::nullptr_t) : ns_(nullptr) {}
~NamespaceGuard() { Kill(); }
Namespace* operator->() const { return ns_.get(); }
fxl::RefPtr<Namespace>& ns() { return ns_; }
void Kill() {
if (ns_) {
ns_->FlushAndShutdown(ns_);
}
ns_ = nullptr;
}
private:
fxl::RefPtr<Namespace> ns_;
};
class RealmTest : public gtest::TestWithEnvironmentFixture {
protected:
RealmTest() {}
NamespaceGuard MakeNamespace(ServiceListPtr additional_services,
NamespaceGuard parent = nullptr) {
if (parent.ns().get() == nullptr) {
return NamespaceGuard(
fxl::MakeRefCounted<Namespace>(nullptr, std::move(additional_services), nullptr));
}
return NamespaceGuard(Namespace::CreateChildNamespace(parent.ns(), nullptr,
std::move(additional_services), nullptr));
}
zx_status_t AddService(const std::string& name) {
auto cb = [this, name](zx::channel channel, async_dispatcher_t* dispatcher) {
++connection_ctr_[name];
};
return directory_.AddEntry(name, std::make_unique<vfs::Service>(cb));
}
vfs::PseudoDir directory_;
std::map<std::string, int> connection_ctr_;
};
// This test checks that if the process can not be created that structures are cleaned up.
TEST_F(RealmTest, ProcessCreationFailure) {
// Create a namespace to be used for the component.
ServiceListPtr service_list(new ServiceList);
static constexpr char kService1[] = "fuchsia.test.TestService1";
static constexpr char kService2[] = "fuchsia.test.TestService2";
service_list->names.push_back(kService1);
service_list->names.push_back(kService2);
AddService(kService1);
AddService(kService2);
auto ns = MakeNamespace(std::move(service_list));
fdio_ns_t* fdio_ns;
fdio_ns_create(&fdio_ns);
zx::channel ch0, ch1;
auto status = zx::channel::create(0, &ch0, &ch1);
ASSERT_EQ(status, ZX_OK);
static constexpr char kNSService1[] = "/svc/fuchsia.ns.TestService";
fdio_ns_bind(fdio_ns, kNSService1, ch1.get());
fdio_flat_namespace_t* flat_ns;
fdio_ns_export(fdio_ns, &flat_ns);
zx_handle_t job = zx_job_default();
zx_handle_t child;
status = zx_job_create(job, 0, &child);
ASSERT_EQ(status, ZX_OK);
zx_handle_t child_dupe;
status = zx_handle_duplicate(child, ZX_RIGHT_SAME_RIGHTS, &child_dupe);
ASSERT_EQ(status, ZX_OK);
zx::job child_job = zx::job(child);
zx::channel exception_channel;
child_job.create_exception_channel(0, &exception_channel);
// Create a child process object, but don't actually create a process.
zx::process child_process = zx::process();
ASSERT_FALSE(child_process);
// Pass in some placeholder values, since the process is invalidd, nothing
// here will actually be used.
Realm* no_realm = nullptr;
std::string args = "";
fuchsia::sys::ComponentControllerPtr component_controller;
ComponentRequestWrapper component_req =
ComponentRequestWrapper(component_controller.NewRequest());
std::string url = "";
ExportedDirChannels channels = ExportedDirChannels();
fit::function<void(std::weak_ptr<ComponentControllerImpl> component)> callback =
[](std::weak_ptr<ComponentControllerImpl> component) {};
zx::channel pkg_hnd;
ASSERT_EQ(ns.ns()->status(), Namespace::Status::RUNNING);
zx_info_job_t job_info;
size_t info_actual, info_available;
status = zx_object_get_info(child_dupe, ZX_INFO_JOB, &job_info, sizeof(zx_info_job_t),
&info_actual, &info_available);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(false, job_info.exited);
// Execute an immediate wait on all the flat namespace handles to check they
// are valid.
for (size_t i = 0; i < flat_ns->count; i++) {
zx_signals_t observed;
status = zx_object_wait_one(flat_ns->handle[i], ZX_CHANNEL_PEER_CLOSED, ZX_TIME_INFINITE_PAST,
&observed);
ASSERT_NE(ZX_ERR_BAD_HANDLE, status);
}
Realm::InstallRuntime(no_realm, std::move(child_job), std::move(child_process), ns.ns(), flat_ns,
args, std::move(component_req), url, std::move(channels),
std::move(callback), std::move(pkg_hnd), std::move(exception_channel));
// Check that all the things we expect to be torn down are torn down.
// The job should have exited.
status = zx_object_get_info(child_dupe, ZX_INFO_JOB, &job_info, sizeof(zx_info_job_t),
&info_actual, &info_available);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(true, job_info.exited);
// Execute a wait that will return immediately and expect all theh handles to
// be be invalid since they should have been closed.
for (size_t i = 0; i < flat_ns->count; i++) {
zx_signals_t observed;
status = zx_object_wait_one(flat_ns->handle[i], ZX_CHANNEL_PEER_CLOSED, ZX_TIME_INFINITE_PAST,
&observed);
ASSERT_EQ(ZX_ERR_BAD_HANDLE, status);
}
// The namespace should not be marked running.
ASSERT_NE(ns.ns()->status(), Namespace::Status::RUNNING);
fdio_ns_free_flat_ns(flat_ns);
fdio_ns_destroy(fdio_ns);
}
} // namespace
} // namespace component