| // 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)); |
| } |