| // Copyright 2022 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| use anyhow::{Context, Error}; | |
| use argh::FromArgs; | |
| use fidl_fuchsia_sysinfo::{InterruptControllerInfo, SysInfoMarker, SysInfoProxy}; | |
| use fuchsia_component::client::connect_to_protocol; | |
| use futures as _; | |
| use std::fmt::Debug; | |
| use std::io::Write; | |
| #[derive(Debug, Clone, Copy, PartialEq)] | |
| enum SystemInfo { | |
| BoardName, | |
| BoardRevision, | |
| BootLoaderVendor, | |
| InterruptController, | |
| SerialNumber, | |
| } | |
| impl std::str::FromStr for SystemInfo { | |
| type Err = String; | |
| fn from_str(value: &str) -> Result<Self, Self::Err> { | |
| match value { | |
| "board_name" => Ok(SystemInfo::BoardName), | |
| "board_revision" => Ok(SystemInfo::BoardRevision), | |
| "bootloader_vendor" => Ok(SystemInfo::BootLoaderVendor), | |
| "interrupt_controller" => Ok(SystemInfo::InterruptController), | |
| "serial_number" => Ok(SystemInfo::SerialNumber), | |
| _ => Err(format!("Invalid key: {:?}. ", value)), | |
| } | |
| } | |
| } | |
| fn unwrap_option<T: ToString>(info: Option<T>) -> String { | |
| info.map(|v| v.to_string()).unwrap_or_else(|| "None".to_string()) | |
| } | |
| fn unwrap_option_debug<T: Debug>(t: Option<T>) -> String { | |
| t.map(|t| format!("{:?}", t)).unwrap_or_else(|| "None".to_string()) | |
| } | |
| fn write_board_name_info<W: Write>( | |
| w: &mut W, | |
| board_name_info: (i32, Option<String>), | |
| ) -> Result<(), Error> { | |
| if let Err(status) = zx::Status::ok(board_name_info.0) { | |
| writeln!(w, "zx_status: {}", status)?; | |
| } | |
| writeln!(w, "board_name: {}", unwrap_option(board_name_info.1))?; | |
| Ok(()) | |
| } | |
| async fn print_board_name_info(provider: SysInfoProxy) -> Result<(), Error> { | |
| let board_name_info = provider.get_board_name().await?; | |
| let mut w = std::io::stdout(); | |
| write_board_name_info(&mut w, board_name_info) | |
| .expect("Function write_board_name_info() failed"); | |
| Ok(()) | |
| } | |
| fn write_board_revision_info<W: Write>( | |
| w: &mut W, | |
| board_revision_info: (i32, u32), | |
| ) -> Result<(), Error> { | |
| if let Err(status) = zx::Status::ok(board_revision_info.0) { | |
| writeln!(w, "zx_status: {}", status)?; | |
| } | |
| writeln!(w, "board_revision: {}", board_revision_info.1)?; | |
| Ok(()) | |
| } | |
| async fn print_board_revision_info(provider: SysInfoProxy) -> Result<(), Error> { | |
| let board_revision_info = provider.get_board_revision().await?; | |
| let mut w = std::io::stdout(); | |
| write_board_revision_info(&mut w, board_revision_info) | |
| .expect("Function write_board_revision_info() failed"); | |
| Ok(()) | |
| } | |
| fn write_bootloader_vendor_info<W: Write>( | |
| w: &mut W, | |
| bootloader_vendor_info: (i32, Option<String>), | |
| ) -> Result<(), Error> { | |
| if let Err(status) = zx::Status::ok(bootloader_vendor_info.0) { | |
| writeln!(w, "zx_status: {}", status)?; | |
| } | |
| writeln!(w, "bootloader_vendor: {}", unwrap_option(bootloader_vendor_info.1))?; | |
| Ok(()) | |
| } | |
| async fn print_bootloader_vendor_info(provider: SysInfoProxy) -> Result<(), Error> { | |
| let bootloader_vendor_info = provider.get_bootloader_vendor().await?; | |
| let mut w = std::io::stdout(); | |
| write_bootloader_vendor_info(&mut w, bootloader_vendor_info) | |
| .expect("Function write_bootloader_vendor_info() failed"); | |
| Ok(()) | |
| } | |
| fn write_interrupt_controller_info<W: Write>( | |
| w: &mut W, | |
| interrupt_controller_info: (i32, Option<Box<InterruptControllerInfo>>), | |
| ) -> Result<(), Error> { | |
| if let Err(status) = zx::Status::ok(interrupt_controller_info.0) { | |
| writeln!(w, "zx_status: {}", status)?; | |
| } | |
| writeln!( | |
| w, | |
| "interrupt_controller: {}", | |
| unwrap_option_debug(interrupt_controller_info.1.map(|t| t.type_)) | |
| )?; | |
| Ok(()) | |
| } | |
| async fn print_interrupt_controller_info(provider: SysInfoProxy) -> Result<(), Error> { | |
| let interrupt_controller_info = provider.get_interrupt_controller_info().await?; | |
| let mut w = std::io::stdout(); | |
| write_interrupt_controller_info(&mut w, interrupt_controller_info) | |
| .expect("Function write_interrupt_controller_info() failed"); | |
| Ok(()) | |
| } | |
| fn write_serial_number<W: Write>( | |
| w: &mut W, | |
| serial_number: Result<String, i32>, | |
| ) -> Result<(), Error> { | |
| match serial_number { | |
| Ok(s) => writeln!(w, "serial_number: {}", s)?, | |
| Err(e) => writeln!(w, "zx_status: {}", zx::Status::err_from_raw(e))?, | |
| } | |
| Ok(()) | |
| } | |
| async fn print_serial_number(provider: SysInfoProxy) -> Result<(), Error> { | |
| let serial_number = provider.get_serial_number().await?; | |
| let mut w = std::io::stdout(); | |
| write_serial_number(&mut w, serial_number).expect("Function write_serial_number() failed"); | |
| Ok(()) | |
| } | |
| /// System Information command. | |
| #[derive(Debug, PartialEq, FromArgs)] | |
| struct BuildInfoCmd { | |
| /// valid keys: <board_name> <board_revision> <bootloader_vendor> <interrupt_controller> <serial_number> | |
| #[argh(positional)] | |
| info: SystemInfo, | |
| } | |
| #[fuchsia::main] | |
| async fn main() -> Result<(), Error> { | |
| let args: BuildInfoCmd = argh::from_env(); | |
| let provider = connect_to_protocol::<SysInfoMarker>() | |
| .context("Failed to connect to the board info service")?; | |
| match args.info { | |
| SystemInfo::BoardName => print_board_name_info(provider).await?, | |
| SystemInfo::BoardRevision => print_board_revision_info(provider).await?, | |
| SystemInfo::BootLoaderVendor => print_bootloader_vendor_info(provider).await?, | |
| SystemInfo::InterruptController => print_interrupt_controller_info(provider).await?, | |
| SystemInfo::SerialNumber => print_serial_number(provider).await?, | |
| } | |
| Ok(()) | |
| } | |
| // =========== Testing the sys-info output =================== | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| use fidl_fuchsia_sysinfo::InterruptControllerType; | |
| #[fuchsia::test] | |
| fn test_format_board_name_info() { | |
| // Tests with the BoardName fields set to data. | |
| let board_name_info = (0, Some("butterfly".to_string())); | |
| let mut v = Vec::new(); | |
| write_board_name_info(&mut v, board_name_info) | |
| .expect("Testing write_board_name_info() failed"); | |
| let write_str = String::from_utf8(v).expect("Failed to convert to string"); | |
| let expect_str = concat!("board_name: butterfly\n"); | |
| // Verify expressions match | |
| assert_eq!(write_str, expect_str); | |
| // Tests with the BoardName fields set to None. | |
| let board_name_info_none = (0, None); | |
| let mut v_none = Vec::new(); | |
| write_board_name_info(&mut v_none, board_name_info_none) | |
| .expect("Testing write_board_name_info() failed"); | |
| let write_str_none = String::from_utf8(v_none).expect("Failed to convert to string"); | |
| let expect_str_none = concat!("board_name: None\n"); | |
| // Verify expression match | |
| assert_eq!(write_str_none, expect_str_none); | |
| } | |
| #[fuchsia::test] | |
| fn test_format_board_revision_info() { | |
| // Tests with the BoardRevision fields set to data. | |
| let board_revision_info = (0, 100); | |
| let mut v = Vec::new(); | |
| write_board_revision_info(&mut v, board_revision_info) | |
| .expect("Testing write_board_revision_info() failed"); | |
| let write_str = String::from_utf8(v).expect("Failed to convert to string"); | |
| let expect_str = concat!("board_revision: 100\n"); | |
| // Verify expressions match | |
| assert_eq!(write_str, expect_str); | |
| } | |
| #[fuchsia::test] | |
| fn test_format_bootloader_vendor_info() { | |
| // Tests with the BootloaderVendor fields set to data. | |
| let bootloader_vendor_info = (0, Some("vendor-101".to_string())); | |
| let mut v = Vec::new(); | |
| write_bootloader_vendor_info(&mut v, bootloader_vendor_info) | |
| .expect("Testing write_bootloader_vendor_info() failed"); | |
| let write_str = String::from_utf8(v).expect("Failed to convert to string"); | |
| let expect_str = concat!("bootloader_vendor: vendor-101\n"); | |
| // Verify expressions match | |
| assert_eq!(write_str, expect_str); | |
| // Tests with the BootloaderVendor fields set to None. | |
| let bootloader_vendor_info_none = (0, None); | |
| let mut v_none = Vec::new(); | |
| write_bootloader_vendor_info(&mut v_none, bootloader_vendor_info_none) | |
| .expect("Testing write_bootloader_vendor_info() failed"); | |
| let write_str_none = String::from_utf8(v_none).expect("Failed to convert to string"); | |
| let expect_str_none = concat!("bootloader_vendor: None\n"); | |
| // Verify expression match | |
| assert_eq!(write_str_none, expect_str_none); | |
| } | |
| #[fuchsia::test] | |
| fn test_format_interrupt_controller_info() { | |
| // Tests with the InterruptControllerInfo fields set to data. | |
| let interrupt_info = InterruptControllerInfo { type_: InterruptControllerType::Apic }; | |
| let interrupt_controller_info = (0, Some(Box::new(interrupt_info))); | |
| let mut v = Vec::new(); | |
| write_interrupt_controller_info(&mut v, interrupt_controller_info) | |
| .expect("Testing write_interrupt_controller_info() failed"); | |
| let write_str = String::from_utf8(v).expect("Failed to convert to string"); | |
| let expect_str = concat!("interrupt_controller: Apic\n"); | |
| // Verify expressions match | |
| assert_eq!(write_str, expect_str); | |
| // Tests with the InterruptControllerInfo fields set to None. | |
| let interrupt_controller_info_none = (0, None); | |
| let mut v_none = Vec::new(); | |
| write_interrupt_controller_info(&mut v_none, interrupt_controller_info_none) | |
| .expect("Testing write_interrupt_controller_info() failed"); | |
| let write_str_none = String::from_utf8(v_none).expect("Failed to convert to string"); | |
| let expect_str_none = concat!("interrupt_controller: None\n"); | |
| // Verify expression match | |
| assert_eq!(write_str_none, expect_str_none); | |
| } | |
| } |