1use fidl_fuchsia_net as fnet;
10use fidl_fuchsia_posix_socket::{self as fposix_socket, OptionalUint32};
11use fuchsia_async as fasync;
12use fuchsia_component::server::{ServiceFs, ServiceFsDir};
13use fuchsia_inspect::health::Reporter;
14use fuchsia_inspect_derive::{IValue, Inspect, Unit, WithInspect as _};
15use futures::StreamExt as _;
16use futures::lock::Mutex;
17use log::error;
18use std::sync::Arc;
19
20mod mark_watcher;
21mod socket_provider;
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24struct SocketMarks {
25 mark_1: OptionalUint32,
26 mark_2: OptionalUint32,
27}
28
29impl From<fnet::Marks> for SocketMarks {
30 fn from(fnet::Marks { mark_1, mark_2, __source_breaking }: fnet::Marks) -> Self {
31 let into_optional_uint32 = |opt| match opt {
32 Some(val) => OptionalUint32::Value(val),
33 None => OptionalUint32::Unset(fposix_socket::Empty),
34 };
35 Self { mark_1: into_optional_uint32(mark_1), mark_2: into_optional_uint32(mark_2) }
36 }
37}
38
39impl From<SocketMarks> for fnet::Marks {
40 fn from(SocketMarks { mark_1, mark_2 }: SocketMarks) -> Self {
41 let into_option_u32 = |opt| match opt {
42 OptionalUint32::Unset(fposix_socket::Empty) => None,
43 OptionalUint32::Value(val) => Some(val),
44 };
45 Self {
46 mark_1: into_option_u32(mark_1),
47 mark_2: into_option_u32(mark_2),
48 __source_breaking: fidl::marker::SourceBreaking,
49 }
50 }
51}
52
53impl SocketMarks {
54 fn has_value(&self) -> bool {
55 match (self.mark_1, self.mark_2) {
56 (OptionalUint32::Value(_), _) => true,
57 (_, OptionalUint32::Value(_)) => true,
58 _ => false,
59 }
60 }
61}
62
63impl Default for SocketMarks {
64 fn default() -> Self {
65 Self {
66 mark_1: OptionalUint32::Unset(fposix_socket::Empty),
67 mark_2: OptionalUint32::Unset(fposix_socket::Empty),
68 }
69 }
70}
71
72impl Unit for SocketMarks {
73 type Data = fuchsia_inspect::Node;
74
75 fn inspect_create(&self, parent: &fuchsia_inspect::Node, name: impl AsRef<str>) -> Self::Data {
76 let mut node = parent.create_child(name.as_ref());
77 self.inspect_update(&mut node);
78 node
79 }
80
81 fn inspect_update(&self, data: &mut Self::Data) {
82 data.atomic_update(|node| {
83 node.clear_recorded();
84 let fnet::Marks { mark_1, mark_2, __source_breaking } = fnet::Marks::from(*self);
85 if let Some(mark_1) = mark_1 {
86 node.record_uint("mark_1", mark_1.into());
87 }
88 if let Some(mark_2) = mark_2 {
89 node.record_uint("mark_2", mark_2.into());
90 }
91 node.record_bool("has_mark", self.has_value());
92 });
93 }
94}
95
96impl std::fmt::Display for SocketMarks {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self.mark_1 {
99 fposix_socket::OptionalUint32::Value(v) => write!(f, "{v}"),
100 fposix_socket::OptionalUint32::Unset(fposix_socket::Empty) => write!(f, "None"),
101 }
102 }
103}
104
105#[derive(Inspect)]
106struct SocketProxy {
107 marks: Arc<Mutex<IValue<SocketMarks>>>,
108 socket_provider: socket_provider::SocketProvider,
109}
110
111impl SocketProxy {
112 fn new() -> Self {
113 let marks = Arc::new(Mutex::new(IValue::new(SocketMarks::default())));
114 Self { marks: marks.clone(), socket_provider: socket_provider::SocketProvider::new(marks) }
115 }
116
117 async fn set_marks(&self, marks: SocketMarks) {
118 self.marks.lock().await.iset(marks);
119 }
120}
121
122enum IncomingService {
123 PosixSocket(fidl_fuchsia_posix_socket::ProviderRequestStream),
124 PosixSocketRaw(fidl_fuchsia_posix_socket_raw::ProviderRequestStream),
125}
126
127pub async fn run() -> Result<(), anyhow::Error> {
129 fuchsia_inspect::component::health().set_starting_up();
130
131 let inspector = fuchsia_inspect::component::inspector();
132 let _inspect_server_task =
133 inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default());
134
135 let proxy = Arc::new(SocketProxy::new().with_inspect(inspector.root(), "root")?);
136
137 let mut fs = ServiceFs::new_local();
138 let _: &mut ServiceFsDir<'_, _> = fs
139 .dir("svc")
140 .add_fidl_service(IncomingService::PosixSocket)
141 .add_fidl_service(IncomingService::PosixSocketRaw);
142
143 let _: &mut ServiceFs<_> = fs.take_and_serve_directory_handle()?;
144
145 fuchsia_inspect::component::health().set_ok();
146
147 let proxy_for_service = Arc::clone(&proxy);
148 let service_fut = fs.for_each_concurrent(100, move |service| {
149 let proxy = Arc::clone(&proxy_for_service);
150 async move {
151 match service {
152 IncomingService::PosixSocket(stream) => proxy.socket_provider.run(stream).await,
153 IncomingService::PosixSocketRaw(stream) => {
154 proxy.socket_provider.run_raw(stream).await
155 }
156 }
157 .unwrap_or_else(|e| error!("{e:?}"))
158 }
159 });
160
161 let scope = fasync::Scope::new();
162
163 let proxy_clone = Arc::clone(&proxy);
164 let _ = scope.spawn_local(async move {
165 mark_watcher::watch_properties(proxy_clone).await;
166 });
167
168 let _ = scope.spawn_local(async move {
169 service_fut.await;
170 error!("The main services future has terminated. It should never terminate");
171 fasync::Scope::current().abort().await;
173 });
174
175 scope.join().await;
176
177 Ok(())
178}
179#[cfg(test)]
180mod test {
181 use super::*;
182 use diagnostics_assertions::assert_data_tree;
183
184 const TEST_MARK_1: u32 = 42;
185 const TEST_MARK_2: u32 = 99;
186
187 #[fuchsia::test]
188 async fn test_socket_proxy_set_marks() {
189 let inspector = fuchsia_inspect::Inspector::default();
190 let proxy = SocketProxy::new().with_inspect(inspector.root(), "root").expect("attach");
191
192 assert_data_tree!(inspector, root: contains {
193 marks: {
194 has_mark: false,
195 },
196 });
197
198 let marks = SocketMarks {
199 mark_1: OptionalUint32::Value(TEST_MARK_1),
200 mark_2: OptionalUint32::Unset(fposix_socket::Empty),
201 };
202 proxy.set_marks(marks).await;
203 assert_eq!(**proxy.marks.lock().await, marks);
204 assert_data_tree!(inspector, root: contains {
205 marks: {
206 mark_1: u64::from(TEST_MARK_1),
207 has_mark: true,
208 },
209 });
210
211 let both_marks = SocketMarks {
212 mark_1: OptionalUint32::Value(TEST_MARK_1),
213 mark_2: OptionalUint32::Value(TEST_MARK_2),
214 };
215 proxy.set_marks(both_marks).await;
216 assert_eq!(**proxy.marks.lock().await, both_marks);
217 assert_data_tree!(inspector, root: contains {
218 marks: {
219 mark_1: u64::from(TEST_MARK_1),
220 mark_2: u64::from(TEST_MARK_2),
221 has_mark: true,
222 },
223 });
224
225 proxy.set_marks(SocketMarks::default()).await;
226 assert_eq!(**proxy.marks.lock().await, SocketMarks::default());
227 assert_data_tree!(inspector, root: contains {
228 marks: {
229 has_mark: false,
230 },
231 });
232 }
233}