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