1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <utility>
#include <vector>
#include "base/at_exit.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/task/single_thread_task_executor.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/core/ipcz_api.h"
#include "mojo/core/ipcz_driver/transport.h"
#include "mojo/core/scoped_ipcz_handle.h"
#include "mojo/proxy/node_proxy.h"
#include "mojo/proxy/portal_proxy.h"
#include "mojo/proxy/switches.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/platform/platform_handle.h"
#include "mojo/public/cpp/system/invitation.h"
#include "third_party/ipcz/include/ipcz/ipcz.h"
namespace mojo_proxy {
void RunProxy(int argc, char** argv) {
CHECK(base::CommandLine::Init(argc, argv));
base::AtExitManager at_exit;
logging::InitLogging({});
logging::SetLogItems(true, true, true, true);
base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
// We initialize Mojo with ipcz disabled, since pre-ipcz Mojo Core only works
// as a process-wide singleton. This means that all Mojo C APIs in this
// process are wired to the old Mojo implementation and are therefore usable
// to interface (exclusively) with the proxy's legacy client.
//
// We always operate as a broker on the legacy side based on the assumption
// that all legacy clients are non-brokers. We're the only node the legacy
// client communicates with.
mojo::core::Configuration mojo_config;
mojo_config.is_broker_process = true;
mojo_config.disable_ipcz = true;
mojo::core::Init(mojo_config);
at_exit.RegisterTask(base::BindOnce(&mojo::core::ShutDown));
auto ipc_support = std::make_unique<mojo::core::ScopedIPCSupport>(
io_task_executor.task_runner(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
// Also initialize the global MojoIpcz node, but don't re-initialize Mojo
// Core. Mojo C APIs therefore still point to the old Mojo implementation, and
// any interaction with the MojoIpcz side of the proxy must be done direct
// calls into either ipcz or the MojoIpcz driver.
//
// On the ipcz side we're a non-broker, based on the assumption that either
// our ipcz client is a broker or (if --inherit-ipcz-broker is given) we can
// inherit a broker from them.
mojo::core::IpczNodeOptions ipcz_options{
.is_broker = false,
.use_local_shared_memory_allocation = true,
};
CHECK(mojo::core::InitializeIpczNodeForProcess(ipcz_options));
const IpczHandle ipcz_node = mojo::core::GetIpczNode();
int fd;
const auto& command_line = *base::CommandLine::ForCurrentProcess();
CHECK(base::StringToInt(
command_line.GetSwitchValueASCII(switches::kLegacyClientFd), &fd));
mojo::PlatformChannelEndpoint legacy_endpoint{
mojo::PlatformHandle{base::ScopedFD{fd}}};
CHECK(base::StringToInt(
command_line.GetSwitchValueASCII(switches::kHostIpczTransportFd), &fd));
mojo::PlatformChannelEndpoint ipcz_endpoint{
mojo::PlatformHandle{base::ScopedFD{fd}}};
// Some Mojo clients use free-form strings for attachment names, and some use
// 64-bit integral, zero-based values. In general only the latter cases attach
// multiple pipes to a single invitation. The chosen scheme influences how
// MojoIpcz (and therefore how this proxy) maps attachment names from Mojo
// APIs to an index into the portal array filled im by ipcz ConnectNode().
std::vector<std::string> attachment_names;
if (command_line.HasSwitch(switches::kAttachmentName)) {
attachment_names.push_back(
command_line.GetSwitchValueASCII(switches::kAttachmentName));
} else if (command_line.HasSwitch(switches::kNumAttachments)) {
uint64_t num_unnamed_attachments;
CHECK(base::StringToUint64(
command_line.GetSwitchValueASCII(switches::kNumAttachments),
&num_unnamed_attachments));
for (uint64_t i = 0; i < num_unnamed_attachments; ++i) {
attachment_names.emplace_back(reinterpret_cast<const char*>(&i),
sizeof(i));
}
}
// Create an appropriate ipcz transport to connect back to the host.
using Transport = mojo::core::ipcz_driver::Transport;
const bool inherit_ipcz_broker =
command_line.HasSwitch(switches::kInheritIpczBroker);
const Transport::EndpointType ipcz_client_type =
inherit_ipcz_broker ? Transport::kNonBroker : Transport::kBroker;
auto ipcz_transport = Transport::Create(
{.source = Transport::kNonBroker, .destination = ipcz_client_type},
std::move(ipcz_endpoint), base::Process{});
// Portal 0 is reserved (see below). The portals corresponding to invitation
// attachments span indices [1, N].
const IpczAPI& ipcz = mojo::core::GetIpczAPI();
std::vector<IpczHandle> initial_portals(attachment_names.size() + 1);
const IpczConnectNodeFlags connect_flags =
inherit_ipcz_broker ? IPCZ_CONNECT_NODE_INHERIT_BROKER
: IPCZ_CONNECT_NODE_TO_BROKER;
const IpczResult connect_result = ipcz.ConnectNode(
ipcz_node, Transport::ReleaseAsHandle(std::move(ipcz_transport)),
initial_portals.size(), connect_flags, nullptr, initial_portals.data());
CHECK_EQ(IPCZ_RESULT_OK, connect_result);
// Portal 0 is bound on the other end to an internal shared memory allocation
// service by MojoIpcz. We don't need it.
ipcz.Close(initial_portals[0], IPCZ_NO_FLAGS, nullptr);
// Seed the server with proxies between each of the attached pipes on the
// legacy invitation and their corresponding initial portals from the host
// connection.
base::RunLoop run_loop;
NodeProxy proxy(ipcz, /*dead_callback=*/run_loop.QuitClosure());
mojo::OutgoingInvitation invitation;
for (size_t i = 0; i < attachment_names.size(); ++i) {
proxy.AddPortalProxy(mojo::core::ScopedIpczHandle(initial_portals[i + 1]),
invitation.AttachMessagePipe(attachment_names[i]));
}
// After sending the legacy invitation, we wait until all proxies are dead.
mojo::OutgoingInvitation::Send(std::move(invitation),
base::kNullProcessHandle,
std::move(legacy_endpoint));
run_loop.Run();
mojo::core::DestroyIpczNodeForProcess();
ipc_support.reset();
}
} // namespace mojo_proxy
int main(int argc, char** argv) {
mojo_proxy::RunProxy(argc, argv);
return 0;
}
|