1use fidl::endpoints::{DiscoverableProtocolMarker, Proxy as _, RequestStream as _};
18use fidl_fuchsia_net_stackmigrationdeprecated as fnet_migration;
19use fidl_fuchsia_process_lifecycle as fprocess_lifecycle;
20use fuchsia_async as fasync;
21use futures::{FutureExt as _, StreamExt as _};
22use vfs::directory::helper::DirectlyMutable;
23
24#[fasync::run_singlethreaded]
25pub async fn main() -> std::process::ExitCode {
26 let current_boot_version = {
28 let migration =
29 fuchsia_component::client::connect_to_protocol::<fnet_migration::StateMarker>()
30 .expect("connect to protocol");
31 let fnet_migration::InEffectVersion { current_boot, .. } =
32 migration.get_netstack_version().await.expect("failed to read netstack version");
33 current_boot
34 };
35
36 println!("netstack migration proxy using version {current_boot_version:?}");
37 let bin_path = match current_boot_version {
38 fnet_migration::NetstackVersion::Netstack2 => c"/pkg/bin/netstack",
39 fnet_migration::NetstackVersion::Netstack3 => c"/pkg/bin/netstack3",
40 };
41
42 let ns = fdio::Namespace::installed().expect("failed to get namespace");
43 let mut entries = ns
44 .export()
45 .expect("failed to export namespace entries")
46 .into_iter()
47 .filter_map(|fdio::NamespaceEntry { handle, path }| match path.as_str() {
48 "/" => {
49 panic!("unexpected non flat namespace, bad capabilities will bleed into netstack")
50 }
51 "/svc" => None,
52 x => {
53 Some((Some(handle), std::ffi::CString::new(x).expect("failed to create C string")))
54 }
55 })
56 .collect::<Vec<_>>();
57
58 let handle =
59 fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleType::DirectoryRequest.into())
60 .expect("missing startup handle");
61
62 let mut actions = vec![fdio::SpawnAction::add_handle(
63 fuchsia_runtime::HandleInfo::new(fuchsia_runtime::HandleType::DirectoryRequest, 0),
64 handle,
65 )];
66
67 actions.extend(entries.iter_mut().map(|(handle, path)| {
68 let handle = handle.take().unwrap();
71 fdio::SpawnAction::add_namespace_entry(path.as_c_str(), handle)
72 }));
73
74 const LIFECYCLE_HANDLE_INFO: fuchsia_runtime::HandleInfo =
75 fuchsia_runtime::HandleInfo::new(fuchsia_runtime::HandleType::Lifecycle, 0);
76 let process_lifecycle = fuchsia_runtime::take_startup_handle(LIFECYCLE_HANDLE_INFO)
77 .expect("missing lifecycle handle");
78
79 let inner_lifecycle_proxy = match current_boot_version {
80 fnet_migration::NetstackVersion::Netstack2 => None,
82 fnet_migration::NetstackVersion::Netstack3 => {
83 let (proxy, server) =
86 fidl::endpoints::create_proxy::<fprocess_lifecycle::LifecycleMarker>();
87 actions.push(fdio::SpawnAction::add_handle(
88 LIFECYCLE_HANDLE_INFO,
89 server.into_channel().into(),
90 ));
91 Some(proxy)
92 }
93 };
94
95 let svc = vfs::directory::immutable::simple::simple();
96 for s in std::fs::read_dir("/svc").expect("failed to get /svc entries") {
97 let entry = s.expect("failed to get directory entry");
98 let name = entry.file_name();
99 let name = name.to_str().expect("failed to get file name");
100
101 let block_services = [
104 fidl_fuchsia_process::LauncherMarker::PROTOCOL_NAME,
105 fnet_migration::StateMarker::PROTOCOL_NAME,
106 ];
107 if block_services.into_iter().any(|s| s == name) {
108 continue;
109 }
110 svc.add_entry(
111 name,
112 vfs::service::endpoint(move |_, channel| {
113 fuchsia_component::client::connect_channel_to_protocol_at_path(
114 channel.into(),
115 entry.path().to_str().expect("failed to get entry path"),
116 )
117 .unwrap_or_else(|e| eprintln!("error connecting to protocol {:?}", e));
118 }),
119 )
120 .unwrap_or_else(|e| panic!("failed to add entry {name}: {e:?}"));
121 }
122
123 let svc_dir = vfs::directory::serve_read_only(svc);
124 let handle = svc_dir.into_client_end().unwrap().into();
125 actions.push(fdio::SpawnAction::add_namespace_entry(c"/svc", handle));
126
127 let config_vmo_handle_info = fuchsia_runtime::HandleType::ComponentConfigVmo.into();
129 if let Some(config_vmo) = fuchsia_runtime::take_startup_handle(config_vmo_handle_info) {
130 actions.push(fdio::SpawnAction::add_handle(config_vmo_handle_info, config_vmo))
131 }
132
133 let log_sink_handle_info = fuchsia_runtime::HandleType::LogSink.into();
134 if let Some(log_sink) = fuchsia_runtime::take_startup_handle(log_sink_handle_info) {
135 actions.push(fdio::SpawnAction::add_handle(log_sink_handle_info, log_sink))
136 }
137
138 let netstack_process = fdio::spawn_etc(
139 &fuchsia_runtime::job_default(),
140 fdio::SpawnOptions::CLONE_ALL - fdio::SpawnOptions::CLONE_NAMESPACE,
141 bin_path,
142 &[bin_path],
143 None,
144 &mut actions[..],
145 )
146 .expect("failed to spawn netstack");
147
148 let mut process_lifecycle = fprocess_lifecycle::LifecycleRequestStream::from_channel(
149 fasync::Channel::from_channel(process_lifecycle.into()).into(),
150 )
151 .filter_map(|r| {
152 futures::future::ready(match r {
153 Ok(r) => Some(r),
154 Err(e) => {
155 eprintln!("process lifecycle FIDL error {e:?}");
156 None
157 }
158 })
159 });
160
161 let mut wait_signals =
162 fasync::OnSignals::new(&netstack_process, zx::Signals::PROCESS_TERMINATED)
163 .map(|s| s.expect("failed to observe process termination signals"));
164 let request = futures::select! {
165 signals = wait_signals => {
166 println!("netstack exited unexpectedly with {signals:?}");
167 return std::process::ExitCode::FAILURE;
168 },
169 r = process_lifecycle.select_next_some() => r,
171 };
172
173 let fprocess_lifecycle::LifecycleRequest::Stop { control_handle } = request;
174 std::mem::drop(control_handle);
177 let (process_lifecycle, _terminated): (_, bool) = process_lifecycle.into_inner().into_inner();
178 let process_lifecycle = std::sync::Arc::try_unwrap(process_lifecycle)
179 .expect("failed to retrieve lifecycle channel");
180 let process_lifecycle: zx::Channel = process_lifecycle.into_channel().into_zx_channel();
181 if let Some(inner) = inner_lifecycle_proxy {
182 inner
183 .stop()
184 .unwrap_or_else(|e| eprintln!("failed to request stop for inner netstack: {e:?}"));
185 std::mem::forget(process_lifecycle);
187 } else {
188 std::mem::drop(process_lifecycle);
191 }
192
193 let signals = wait_signals.await;
194 assert!(signals.contains(zx::Signals::PROCESS_TERMINATED));
195 let zx::ProcessInfo { return_code, .. } =
197 netstack_process.info().expect("reading netstack process info");
198 println!("netstack process exited with return code {return_code}");
199 std::process::exit(return_code.try_into().unwrap_or(std::i32::MIN))
200}