driver_tools/subcommands/list/
mod.rs1pub mod args;
6
7use anyhow::{Context, Result};
8use args::ListCommand;
9use flex_fuchsia_driver_development as fdd;
10#[cfg(feature = "fdomain")]
11use fuchsia_driver_dev_fdomain as fuchsia_driver_dev;
12use futures::join;
13use std::collections::HashSet;
14use std::io::Write;
15
16pub async fn list(
17 cmd: ListCommand,
18 writer: &mut dyn Write,
19 driver_development_proxy: fdd::ManagerProxy,
20) -> Result<()> {
21 if cmd.verbose {
22 writeln!(
23 writer,
24 "WARNING: The verbose flag is deprecated. Use `ffx driver show` instead."
25 )?;
26 return Ok(());
27 }
28
29 let empty: [String; 0] = [];
30 let driver_info = fuchsia_driver_dev::get_driver_info(&driver_development_proxy, &empty);
31
32 let driver_info = if cmd.loaded || cmd.running {
33 let device_info = fuchsia_driver_dev::get_device_info(
35 &driver_development_proxy,
36 &empty,
37 false,
38 );
39
40 let (driver_info, device_info) = join!(driver_info, device_info);
42
43 let loaded_driver_set: HashSet<String> = HashSet::from_iter(
44 device_info?
45 .into_iter()
46 .filter(|device_info| {
47 if cmd.running {
48 device_info.is_running.unwrap_or(false)
49 && device_info.driver_host_koid.is_some()
50 } else {
51 true
52 }
53 })
54 .filter_map(|device_info| device_info.bound_driver_url),
55 );
56
57 driver_info?
59 .into_iter()
60 .filter(|driver| {
61 let mut loaded = false;
62 if let Some(ref url) = driver.url {
63 if loaded_driver_set.contains(url) {
64 loaded = true
65 }
66 }
67 loaded
68 })
69 .collect()
70 } else {
71 driver_info.await.context("Failed to get driver info")?
72 };
73
74 for driver in driver_info {
75 if let Some(name) = driver.name {
76 let url = driver.url.unwrap_or_default();
77 writeln!(writer, "{:<20}: {}", name, url)?;
78 } else {
79 let url = driver.url.unwrap_or_default();
80 writeln!(writer, "{}", url)?;
81 }
82 }
83 Ok(())
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use argh::FromArgs;
90 use flex_client::fidl::ServerEnd;
91 use flex_fuchsia_driver_framework as fdf;
92 use fuchsia_async as fasync;
93 use futures::future::{Future, FutureExt};
94 use futures::stream::StreamExt;
95 #[cfg(feature = "fdomain")]
96 use std::sync::Arc;
97
98 async fn test_list<F, Fut>(
103 #[cfg(feature = "fdomain")] client: Arc<flex_client::Client>,
104 cmd: ListCommand,
105 on_driver_development_request: F,
106 ) -> Result<String>
107 where
108 F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
109 Fut: Future<Output = Result<()>> + Send + Sync,
110 {
111 #[cfg(not(feature = "fdomain"))]
112 let client = flex_client::fidl::ZirconClient;
113 let (driver_development_proxy, mut driver_development_requests) =
114 client.create_proxy_and_stream::<fdd::ManagerMarker>();
115
116 let mut writer = Vec::new();
118 let request_handler_task = fasync::Task::spawn(async move {
119 while let Some(res) = driver_development_requests.next().await {
120 let request = res.unwrap();
121 on_driver_development_request(request).await.context("Failed to handle request")?;
122 }
123 anyhow::bail!("Driver development request stream unexpectedly closed");
124 });
125 futures::select! {
126 res = request_handler_task.fuse() => {
127 res?;
128 anyhow::bail!("Request handler task unexpectedly finished");
129 }
130 res = list(cmd, &mut writer, driver_development_proxy).fuse() => res.context("List command failed")?,
131 }
132
133 String::from_utf8(writer).context("Failed to convert list output to a string")
134 }
135
136 async fn run_driver_info_iterator_server(
137 mut driver_infos: Vec<fdf::DriverInfo>,
138 iterator: ServerEnd<fdd::DriverInfoIteratorMarker>,
139 ) -> Result<()> {
140 let mut iterator = iterator.into_stream();
141 while let Some(res) = iterator.next().await {
142 let request = res.unwrap();
143 match request {
144 fdd::DriverInfoIteratorRequest::GetNext { responder } => {
145 responder.send(&driver_infos).unwrap();
146 driver_infos.clear();
147 }
148 }
149 }
150 Ok(())
151 }
152
153 async fn run_node_info_iterator_server(
154 mut node_infos: Vec<fdd::NodeInfo>,
155 iterator: ServerEnd<fdd::NodeInfoIteratorMarker>,
156 ) -> Result<()> {
157 let mut iterator = iterator.into_stream();
158 while let Some(res) = iterator.next().await {
159 let request = res.unwrap();
160 match request {
161 fdd::NodeInfoIteratorRequest::GetNext { responder } => {
162 responder.send(&node_infos).unwrap();
163 node_infos.clear();
164 }
165 }
166 }
167 Ok(())
168 }
169
170 fn mock_driver_infos() -> Vec<fdf::DriverInfo> {
171 vec![
172 fdf::DriverInfo {
173 name: Some("running".to_owned()),
174 url: Some("fuchsia-pkg://fuchsia.com/running#meta/running.cm".to_owned()),
175 ..Default::default()
176 },
177 fdf::DriverInfo {
178 name: Some("binding".to_owned()),
179 url: Some("fuchsia-pkg://fuchsia.com/binding#meta/binding.cm".to_owned()),
180 ..Default::default()
181 },
182 fdf::DriverInfo {
183 name: Some("starting".to_owned()),
184 url: Some("fuchsia-pkg://fuchsia.com/starting#meta/starting.cm".to_owned()),
185 ..Default::default()
186 },
187 fdf::DriverInfo {
188 name: Some("quarantined".to_owned()),
189 url: Some("fuchsia-pkg://fuchsia.com/quarantined#meta/quarantined.cm".to_owned()),
190 ..Default::default()
191 },
192 fdf::DriverInfo {
193 name: Some("unloaded".to_owned()),
194 url: Some("fuchsia-pkg://fuchsia.com/unloaded#meta/unloaded.cm".to_owned()),
195 ..Default::default()
196 },
197 ]
198 }
199
200 fn mock_node_infos() -> Vec<fdd::NodeInfo> {
201 vec![
202 fdd::NodeInfo {
203 bound_driver_url: Some(
204 "fuchsia-pkg://fuchsia.com/running#meta/running.cm".to_owned(),
205 ),
206 driver_host_koid: Some(12345),
207 is_running: Some(true),
208 quarantined: Some(false),
209 ..Default::default()
210 },
211 fdd::NodeInfo {
212 bound_driver_url: Some(
213 "fuchsia-pkg://fuchsia.com/binding#meta/binding.cm".to_owned(),
214 ),
215 driver_host_koid: Some(12345),
216 is_running: Some(false),
217 quarantined: Some(false),
218 ..Default::default()
219 },
220 fdd::NodeInfo {
221 bound_driver_url: Some(
222 "fuchsia-pkg://fuchsia.com/starting#meta/starting.cm".to_owned(),
223 ),
224 driver_host_koid: None,
225 is_running: Some(false),
226 quarantined: Some(false),
227 ..Default::default()
228 },
229 fdd::NodeInfo {
230 bound_driver_url: Some(
231 "fuchsia-pkg://fuchsia.com/quarantined#meta/quarantined.cm".to_owned(),
232 ),
233 driver_host_koid: None,
234 is_running: Some(false),
235 quarantined: Some(true),
236 ..Default::default()
237 },
238 ]
239 }
240
241 #[fuchsia::test]
242 async fn test_list_all() {
243 #[cfg(feature = "fdomain")]
244 let client = fdomain_local::local_client_empty();
245 let cmd = ListCommand::from_args(&["list"], &[]).unwrap();
246
247 let output = test_list(
248 #[cfg(feature = "fdomain")]
249 client,
250 cmd,
251 |request: fdd::ManagerRequest| async move {
252 match request {
253 fdd::ManagerRequest::GetDriverInfo {
254 driver_filter: _,
255 iterator,
256 control_handle: _,
257 } => run_driver_info_iterator_server(
258 vec![fdf::DriverInfo {
259 name: Some("foo".to_owned()),
260 url: Some(
261 "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
262 ),
263 ..Default::default()
264 }],
265 iterator,
266 )
267 .await
268 .context("Failed to run driver info iterator server")?,
269 _ => {}
270 }
271 Ok(())
272 },
273 )
274 .await
275 .unwrap();
276
277 assert_eq!(
278 output,
279 "foo : fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm\n"
280 );
281 }
282
283 #[fuchsia::test]
284 async fn test_list_loaded() {
285 #[cfg(feature = "fdomain")]
286 let client = fdomain_local::local_client_empty();
287 let cmd = ListCommand::from_args(&["list"], &["--loaded"]).unwrap();
288
289 let output = test_list(
290 #[cfg(feature = "fdomain")]
291 client,
292 cmd,
293 |request: fdd::ManagerRequest| async move {
294 match request {
295 fdd::ManagerRequest::GetDriverInfo {
296 driver_filter: _,
297 iterator,
298 control_handle: _,
299 } => run_driver_info_iterator_server(mock_driver_infos(), iterator)
300 .await
301 .context("Failed to run driver info iterator server")?,
302 fdd::ManagerRequest::GetNodeInfo {
303 node_filter: _,
304 iterator,
305 exact_match: _,
306 control_handle: _,
307 } => run_node_info_iterator_server(mock_node_infos(), iterator)
308 .await
309 .context("Failed to run node info iterator server")?,
310 _ => {}
311 }
312 Ok(())
313 },
314 )
315 .await
316 .unwrap();
317
318 assert_eq!(
319 output,
320 concat!(
321 "running : fuchsia-pkg://fuchsia.com/running#meta/running.cm\n",
322 "binding : fuchsia-pkg://fuchsia.com/binding#meta/binding.cm\n",
323 "starting : fuchsia-pkg://fuchsia.com/starting#meta/starting.cm\n",
324 "quarantined : fuchsia-pkg://fuchsia.com/quarantined#meta/quarantined.cm\n",
325 )
326 );
327 }
328
329 #[fuchsia::test]
330 async fn test_list_running() {
331 #[cfg(feature = "fdomain")]
332 let client = fdomain_local::local_client_empty();
333 let cmd = ListCommand::from_args(&["list"], &["--running"]).unwrap();
334
335 let output = test_list(
336 #[cfg(feature = "fdomain")]
337 client,
338 cmd,
339 |request: fdd::ManagerRequest| async move {
340 match request {
341 fdd::ManagerRequest::GetDriverInfo {
342 driver_filter: _,
343 iterator,
344 control_handle: _,
345 } => run_driver_info_iterator_server(mock_driver_infos(), iterator)
346 .await
347 .context("Failed to run driver info iterator server")?,
348 fdd::ManagerRequest::GetNodeInfo {
349 node_filter: _,
350 iterator,
351 exact_match: _,
352 control_handle: _,
353 } => run_node_info_iterator_server(mock_node_infos(), iterator)
354 .await
355 .context("Failed to run node info iterator server")?,
356 _ => {}
357 }
358 Ok(())
359 },
360 )
361 .await
362 .unwrap();
363
364 assert_eq!(
365 output,
366 "running : fuchsia-pkg://fuchsia.com/running#meta/running.cm\n"
367 );
368 }
369
370 #[fuchsia::test]
371 async fn test_verbose_deprecated() {
372 #[cfg(feature = "fdomain")]
373 let client = fdomain_local::local_client_empty();
374 let cmd = ListCommand::from_args(&["list"], &["--verbose"]).unwrap();
375
376 let output = test_list(
377 #[cfg(feature = "fdomain")]
378 Arc::clone(&client),
379 cmd,
380 |request: fdd::ManagerRequest| async move {
381 match request {
382 fdd::ManagerRequest::GetDriverInfo {
383 driver_filter: _,
384 iterator,
385 control_handle: _,
386 } => run_driver_info_iterator_server(
387 vec![fdf::DriverInfo {
388 name: Some("foo".to_owned()),
389 url: Some(
390 "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
391 ),
392 ..Default::default()
393 }],
394 iterator,
395 )
396 .await
397 .context("Failed to run driver info iterator server")?,
398 _ => {}
399 }
400 Ok(())
401 },
402 )
403 .await
404 .unwrap();
405
406 assert_eq!(
407 output,
408 "WARNING: The verbose flag is deprecated. Use `ffx driver show` instead.\n"
409 );
410 }
411}