driver_tools/subcommands/show/
mod.rs1pub mod args;
6
7use anyhow::Result;
8use args::ShowCommand;
9use bind::debugger::debug_dump::dump_bind_rules;
10use flex_fuchsia_driver_development as fdd;
11use fuchsia_driver_dev::GetSingleDriverError;
12#[cfg(feature = "fdomain")]
13use fuchsia_driver_dev_fdomain as fuchsia_driver_dev;
14use std::collections::HashMap;
15use std::io::Write;
16
17pub async fn show(
18 cmd: ShowCommand,
19 writer: &mut dyn Write,
20 driver_development_proxy: fdd::ManagerProxy,
21) -> Result<()> {
22 let driver_info = match fuchsia_driver_dev::get_single_driver_from_query(
23 &cmd.query,
24 &driver_development_proxy,
25 )
26 .await
27 {
28 Ok(info) => info,
29 Err(GetSingleDriverError::NotFound(query)) => {
30 return Err(anyhow::anyhow!("No drivers found matching {:?}.", query));
31 }
32 Err(e) => return Err(anyhow::anyhow!(e)),
33 };
34
35 let empty: [String; 0] = [];
37 let device_info = fuchsia_driver_dev::get_device_info(
38 &driver_development_proxy,
39 &empty,
40 false,
41 )
42 .await?;
43
44 let mut driver_to_devices = HashMap::<String, Vec<String>>::new();
45 for device in device_info.into_iter() {
46 let driver = device.bound_driver_url.clone();
47 let device_name = device.moniker.as_ref();
48 if let (Some(driver), Some(device_name)) = (driver, device_name) {
49 driver_to_devices
50 .entry(driver.to_string())
51 .and_modify(|v| v.push(device_name.to_string()))
52 .or_insert_with(|| vec![device_name.to_string()]);
53 }
54 }
55
56 if let Some(name) = driver_info.name {
57 writeln!(writer, "{0: <10}: {1}", "Name", name)?;
58 }
59 if let Some(url) = driver_info.url.as_ref() {
60 writeln!(writer, "{0: <10}: {1}", "URL", url)?;
61 }
62
63 writeln!(
65 writer,
66 "{0: <10}: {1}",
67 "DF Version",
68 driver_info.driver_framework_version.unwrap_or(1)
69 )?;
70
71 if let Some(node_categories) = driver_info.device_categories {
72 write!(writer, "Node Categories: [")?;
73
74 for (i, category_table) in node_categories.iter().enumerate() {
75 if let Some(category) = &category_table.category {
76 if let Some(subcategory) = &category_table.subcategory {
77 if !subcategory.is_empty() {
78 write!(writer, "{}::{}", category, subcategory)?;
79 } else {
80 write!(writer, "{}", category,)?;
81 }
82 } else {
83 write!(writer, "{}", category,)?;
84 }
85 }
86
87 if i != node_categories.len() - 1 {
88 write!(writer, ", ")?;
89 }
90 }
91 writeln!(writer, "]")?;
92 }
93
94 if let Some(url) = driver_info.url {
95 if let Some(nodes) = driver_to_devices.get(&url) {
96 writeln!(writer, "{0: <10}:\n {1}", "Nodes", nodes.join("\n "))?;
97 }
98 }
99 let bind_rules =
100 driver_info.bind_rules_bytecode.map(|bytecode| dump_bind_rules(bytecode).ok()).flatten();
101 match bind_rules {
102 Some(bind_rules) => {
103 writeln!(writer, "{0: <10}: ", "Bind rules bytecode")?;
104 writeln!(writer, "{}", bind_rules)?;
105 }
106 _ => writeln!(writer, "Issue parsing the bind rules bytecode")?,
107 }
108 writeln!(writer)?;
109
110 Ok(())
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use anyhow::Context;
117 use argh::FromArgs;
118 use flex_client::fidl::ServerEnd;
119 use flex_fuchsia_driver_framework as fdf;
120 use fuchsia_async as fasync;
121 use futures::future::{Future, FutureExt};
122 use futures::stream::StreamExt;
123
124 async fn run_test_show<F, Fut>(
129 #[cfg(feature = "fdomain")] client: std::sync::Arc<flex_client::Client>,
130 cmd: ShowCommand,
131 on_driver_development_request: F,
132 ) -> Result<String>
133 where
134 F: Fn(fdd::ManagerRequest) -> Fut + Send + Sync + 'static,
135 Fut: Future<Output = Result<()>> + Send + Sync,
136 {
137 #[cfg(not(feature = "fdomain"))]
138 let client = flex_client::fidl::ZirconClient;
139 let (driver_development_proxy, mut driver_development_requests) =
140 client.create_proxy_and_stream::<fdd::ManagerMarker>();
141
142 let mut writer = Vec::new();
144 let request_handler_task = fasync::Task::spawn(async move {
145 while let Some(res) = driver_development_requests.next().await {
146 let request = res.context("Failed to get next request")?;
147 on_driver_development_request(request).await.context("Failed to handle request")?;
148 }
149 anyhow::bail!("Driver development request stream unexpectedly closed");
150 });
151 futures::select! {
152 res = request_handler_task.fuse() => {
153 res?;
154 anyhow::bail!("Request handler task unexpectedly finished");
155 }
156 res = show(cmd, &mut writer, driver_development_proxy).fuse() => res.context("Show command failed")?,
157 }
158
159 String::from_utf8(writer).context("Failed to convert show output to a string")
160 }
161
162 async fn run_driver_info_iterator_server(
163 mut driver_infos: Vec<fdf::DriverInfo>,
164 iterator: ServerEnd<fdd::DriverInfoIteratorMarker>,
165 ) -> Result<()> {
166 let mut iterator = iterator.into_stream();
167 while let Some(res) = iterator.next().await {
168 let request = res.context("Failed to get request")?;
169 match request {
170 fdd::DriverInfoIteratorRequest::GetNext { responder } => {
171 responder
172 .send(&driver_infos)
173 .context("Failed to send driver infos to responder")?;
174 driver_infos.clear();
175 }
176 }
177 }
178 Ok(())
179 }
180
181 async fn run_device_info_iterator_server(
182 mut device_infos: Vec<fdd::NodeInfo>,
183 iterator: ServerEnd<fdd::NodeInfoIteratorMarker>,
184 ) -> Result<()> {
185 let mut iterator = iterator.into_stream();
186 while let Some(res) = iterator.next().await {
187 let request = res.context("Failed to get request")?;
188 match request {
189 fdd::NodeInfoIteratorRequest::GetNext { responder } => {
190 responder
191 .send(&device_infos)
192 .context("Failed to send device infos to responder")?;
193 device_infos.clear();
194 }
195 }
196 }
197 Ok(())
198 }
199
200 #[fuchsia::test]
201 async fn test_show() {
202 #[cfg(feature = "fdomain")]
203 let client = fdomain_local::local_client_empty();
204 let cmd = ShowCommand::from_args(&["show"], &["foo"]).unwrap();
205
206 let output = run_test_show(
207 #[cfg(feature = "fdomain")]
208 client,
209 cmd,
210 |request: fdd::ManagerRequest| async move {
211 match request {
212 fdd::ManagerRequest::GetDriverInfo {
213 driver_filter,
214 iterator,
215 control_handle: _,
216 } => {
217 let mut infos = vec![fdf::DriverInfo {
218 name: Some("foo".to_owned()),
219 url: Some(
220 "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
221 ),
222 package_type: Some(fdf::DriverPackageType::Base),
223 device_categories: Some(vec![
224 fdf::DeviceCategory {
225 category: Some("connectivity".to_string()),
226 subcategory: Some("ethernet".to_string()),
227 ..Default::default()
228 },
229 fdf::DeviceCategory {
230 category: Some("usb".to_string()),
231 subcategory: None,
232 ..Default::default()
233 },
234 ]),
235 ..Default::default()
236 }];
237 if !driver_filter.is_empty()
238 && !infos[0].url.as_ref().unwrap().contains(&driver_filter[0])
239 && !infos[0].name.as_ref().unwrap().contains(&driver_filter[0])
240 {
241 infos.clear();
242 }
243
244 run_driver_info_iterator_server(infos, iterator)
245 .await
246 .context("Failed to run driver info iterator server")?
247 }
248 fdd::ManagerRequest::GetNodeInfo {
249 node_filter: _,
250 iterator,
251 control_handle: _,
252 exact_match: _,
253 } => run_device_info_iterator_server(
254 vec![fdd::NodeInfo {
255 bound_driver_url: Some(
256 "fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm".to_owned(),
257 ),
258 moniker: Some("dev.sys.foo".to_owned()),
259 ..Default::default()
260 }],
261 iterator,
262 )
263 .await
264 .context("Failed to run device info iterator server")?,
265 _ => {}
266 }
267 Ok(())
268 },
269 )
270 .await
271 .unwrap();
272
273 assert_eq!(
274 output,
275 r#"Name : foo
276URL : fuchsia-pkg://fuchsia.com/foo-package#meta/foo.cm
277DF Version: 1
278Node Categories: [connectivity::ethernet, usb]
279Nodes :
280 dev.sys.foo
281Issue parsing the bind rules bytecode
282
283"#
284 );
285 }
286
287 #[fuchsia::test]
288 async fn test_show_no_match() {
289 #[cfg(feature = "fdomain")]
290 let client = fdomain_local::local_client_empty();
291 let cmd = ShowCommand::from_args(&["show"], &["nonexistent"]).unwrap();
292
293 let result = run_test_show(
294 #[cfg(feature = "fdomain")]
295 client,
296 cmd,
297 |request: fdd::ManagerRequest| async move {
298 match request {
299 fdd::ManagerRequest::GetDriverInfo { iterator, .. } => {
300 run_driver_info_iterator_server(vec![], iterator).await
301 }
302 _ => Ok(()),
303 }
304 },
305 )
306 .await;
307
308 assert!(result.is_err());
309 assert_eq!(
310 format!("{:?}", result.unwrap_err()),
311 "Show command failed\n\nCaused by:\n No drivers found matching \"nonexistent\"."
312 );
313 }
314
315 #[fuchsia::test]
316 async fn test_show_ambiguous() {
317 #[cfg(feature = "fdomain")]
318 let client = fdomain_local::local_client_empty();
319 let cmd = ShowCommand::from_args(&["show"], &["foo"]).unwrap();
320
321 let result = run_test_show(
322 #[cfg(feature = "fdomain")]
323 client,
324 cmd,
325 |request: fdd::ManagerRequest| async move {
326 match request {
327 fdd::ManagerRequest::GetDriverInfo { iterator, .. } => {
328 run_driver_info_iterator_server(
329 vec![
330 fdf::DriverInfo {
331 name: Some("foo1".to_owned()),
332 url: Some(
333 "fuchsia-pkg://fuchsia.com/foo1#meta/foo1.cm".to_owned(),
334 ),
335 ..Default::default()
336 },
337 fdf::DriverInfo {
338 name: Some("foo2".to_owned()),
339 url: Some(
340 "fuchsia-pkg://fuchsia.com/foo2#meta/foo2.cm".to_owned(),
341 ),
342 ..Default::default()
343 },
344 ],
345 iterator,
346 )
347 .await
348 }
349 _ => Ok(()),
350 }
351 },
352 )
353 .await;
354
355 assert!(result.is_err());
356 let err_msg = format!("{:?}", result.unwrap_err());
357 assert!(err_msg.contains("matches more than one driver"));
358 assert!(err_msg.contains("foo1"));
359 assert!(err_msg.contains("foo2"));
360 }
361}