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
mojo / proxy / main.cc [blame]
// 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;
}