blob: a59f3914a084887ecf9353a5680adf85c2121aae [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 <fidl/fuchsia.posix.socket.packet/cpp/wire.h>
#include <fidl/fuchsia.sys2/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/vfs/cpp/composed_service_dir.h>
#include <lib/zx/channel.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <filesystem>
namespace {
constexpr char kRealmQueryPath[] = "/svc/fuchsia.sys2.RealmQuery.root";
constexpr char kNetstackMoniker[] = "./core/network/netstack";
constexpr char kRootDirectory[] = "/";
constexpr char kServiceDirectory[] = "/svc";
constexpr const char* kPacketSocketProviderName =
fidl::DiscoverableProtocolName<fuchsia_posix_socket_packet::Provider>;
} // namespace
// Attempts to make a packet socket provider available to this program if not
// already available.
//
// The packet socket provider exposed by the core realm's netstack is used if it
// is available.
__attribute__((constructor)) void init_packet_socket_provider() {
std::filesystem::path svc_dir_path(kServiceDirectory);
if (std::filesystem::exists(svc_dir_path / kPacketSocketProviderName)) {
// Packet socket provider is already available.
return;
}
static async::Loop composed_dir_loop(&kAsyncLoopConfigNoAttachToCurrentThread);
// Replace the default service directory with our composed service directory
// to make packet socket provider available to the program and start serving
// requests to the composed service directory.
{
fdio_ns_t* ns;
{
zx_status_t status = fdio_ns_get_installed(&ns);
ZX_ASSERT_MSG(status == ZX_OK, "fdio_ns_get_installed(_): %s", zx_status_get_string(status));
}
constexpr fuchsia::io::OpenFlags kServeFlags(fuchsia::io::OpenFlags::DIRECTORY);
auto bind_to_ns = [ns](const char* path, vfs::ComposedServiceDir& composed_dir) {
zx::channel client, server;
{
zx_status_t status = zx::channel::create(0, &client, &server);
ZX_ASSERT_MSG(status == ZX_OK, "zx::channel::create(0, _, _): %s",
zx_status_get_string(status));
}
{
zx_status_t status = fdio_ns_bind(ns, path, client.release());
ZX_ASSERT_MSG(status == ZX_OK, "fdio_ns_bind(_, %s, _): %s", path,
zx_status_get_string(status));
}
{
zx_status_t status =
composed_dir.Serve(kServeFlags, std::move(server), composed_dir_loop.dispatcher());
ZX_ASSERT_MSG(status == ZX_OK, "composed_dir.Serve(0x%x, _, _): %s",
static_cast<unsigned int>(kServeFlags), zx_status_get_string(status));
}
};
static vfs::ComposedServiceDir composed_svc_dir;
// Our composed service directory should be a superset of the default service
// directory.
{
zx::result original_svc_dir = component::OpenServiceRoot();
switch (zx_status_t status = original_svc_dir.status_value(); status) {
case ZX_OK:
break;
case ZX_ERR_NOT_FOUND:
// Environment didn't populate a service directory for us to use as a
// fallback.
return;
default:
ZX_PANIC("component::OpenServiceRoot(): %s", zx_status_get_string(status));
}
// TODO(https://fxbug.dev/42152501): Avoid this type-unsafe conversion.
composed_svc_dir.set_fallback(
fidl::InterfaceHandle<fuchsia::io::Directory>(original_svc_dir->TakeChannel()));
}
// Add the packet socket provider service to our composed service directory
// by reaching into netstack's exposed directory.
{
zx::result realm_query = component::Connect<fuchsia_sys2::RealmQuery>(kRealmQueryPath);
ZX_ASSERT_MSG(realm_query.is_ok(), "Failed to connect to %s: %s", kRealmQueryPath,
realm_query.status_string());
zx::result endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
ZX_ASSERT_MSG(endpoints.is_ok(), "Failed to create endpoints: %s", endpoints.status_string());
fidl::WireResult open_dir =
fidl::WireCall(realm_query.value())
->Open(kNetstackMoniker, fuchsia_sys2::OpenDirType::kExposedDir,
fuchsia_io::OpenFlags::kRightReadable, fuchsia_io::ModeType(), ".",
std::move(endpoints->server));
ZX_ASSERT_MSG(open_dir.ok(), "Failed to open %s: %s", kNetstackMoniker,
open_dir.status_string());
composed_svc_dir.AddService(
kPacketSocketProviderName,
std::make_unique<vfs::Service>(
[netstack_exposed_dir =
fidl::ClientEnd<fuchsia_io::Directory>(endpoints->client.TakeChannel())](
zx::channel request, async_dispatcher_t* dispatcher) mutable {
zx::result result = component::ConnectAt(
netstack_exposed_dir.borrow(),
fidl::ServerEnd<fuchsia_posix_socket_packet::Provider>(std::move(request)));
ZX_ASSERT_MSG(result.is_ok(), "Failed to connect to packet socker provider: %s",
result.status_string());
}));
}
// Attempt to unbind the service directory from the namespace so we can
// replace it.
switch (zx_status_t status = fdio_ns_unbind(ns, svc_dir_path.c_str()); status) {
// We get |ZX_OK| when the namespace the service directory is a mount
// point.
case ZX_OK:
bind_to_ns(svc_dir_path.c_str(), composed_svc_dir);
break;
// We get |ZX_ERR_BAD_PATH| when the service directory isn't a mount point
// in the namespace (process launched with delayed directories after
// https://fuchsia.googlesource.com/fuchsia/+/82ad8d81396d5659515e830a7364cf33b1605b69).
case ZX_ERR_BAD_PATH: {
std::filesystem::path root(kRootDirectory);
static vfs::ComposedServiceDir composed_root_dir;
{
zx::channel client, server;
{
zx_status_t status = zx::channel::create(0, &client, &server);
ZX_ASSERT_MSG(status == ZX_OK, "zx::channel::create(0, _, _): %s",
zx_status_get_string(status));
}
{
zx_status_t status = fdio_service_connect(root.c_str(), server.release());
ZX_ASSERT_MSG(status == ZX_OK, "fdio_service_connect(%s, _): %s", root.c_str(),
zx_status_get_string(status));
}
composed_root_dir.set_fallback(
fidl::InterfaceHandle<fuchsia::io::Directory>(std::move(client)));
}
composed_root_dir.AddService(
svc_dir_path.filename().c_str(),
std::make_unique<vfs::Service>([](zx::channel request,
async_dispatcher_t* dispatcher) mutable {
zx_status_t status =
composed_svc_dir.Serve(kServeFlags, std::move(request), dispatcher);
ZX_ASSERT_MSG(status == ZX_OK, "composed_svc_dir.Serve(0x%x, _, _): %s",
static_cast<unsigned int>(kServeFlags), zx_status_get_string(status));
}));
{
zx_status_t status = fdio_ns_unbind(ns, root.c_str());
ZX_ASSERT_MSG(status == ZX_OK, "fdio_ns_unbind(_, %s): %s", root.c_str(),
zx_status_get_string(status));
}
bind_to_ns(root.c_str(), composed_root_dir);
} break;
default:
ZX_PANIC("fdio_ns_unbind(_, %s): %s", svc_dir_path.c_str(), zx_status_get_string(status));
}
}
zx_status_t status = composed_dir_loop.StartThread();
ZX_ASSERT_MSG(status == ZX_OK, "Failed to start async loop thread: %s",
zx_status_get_string(status));
}