| // Copyright 2021 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 as _, Result}, |
| fidl_fuchsia_buildinfo as buildinfo, fidl_fuchsia_developer_remotecontrol as rcs, |
| fidl_fuchsia_device as fdevice, fidl_fuchsia_hwinfo as hwinfo, |
| fidl_fuchsia_net_interfaces as fnet_interfaces, |
| fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext, fidl_fuchsia_sysinfo as sysinfo, |
| fuchsia_zircon as zx, |
| std::collections::HashMap, |
| tracing::*, |
| }; |
| |
| pub struct HostIdentifier { |
| pub(crate) interface_state_proxy: fnet_interfaces::StateProxy, |
| pub(crate) name_provider_proxy: fdevice::NameProviderProxy, |
| pub(crate) device_info_proxy: hwinfo::DeviceProxy, |
| pub(crate) system_info_proxy: sysinfo::SysInfoProxy, |
| pub(crate) build_info_proxy: buildinfo::ProviderProxy, |
| pub(crate) boot_timestamp_nanos: u64, |
| } |
| |
| fn connect_to_protocol<P: fidl::endpoints::DiscoverableProtocolMarker>() -> Result<P::Proxy> { |
| fuchsia_component::client::connect_to_protocol::<P>().context(P::DEBUG_NAME) |
| } |
| |
| impl HostIdentifier { |
| pub fn new() -> Result<Self> { |
| let interface_state_proxy = connect_to_protocol::<fnet_interfaces::StateMarker>()?; |
| let name_provider_proxy = connect_to_protocol::<fdevice::NameProviderMarker>()?; |
| let device_info_proxy = connect_to_protocol::<hwinfo::DeviceMarker>()?; |
| let system_info_proxy = connect_to_protocol::<sysinfo::SysInfoMarker>()?; |
| let build_info_proxy = connect_to_protocol::<buildinfo::ProviderMarker>()?; |
| let boot_timestamp_nanos = (fuchsia_runtime::utc_time().into_nanos() |
| - zx::Time::get_monotonic().into_nanos()) as u64; |
| return Ok(Self { |
| interface_state_proxy, |
| name_provider_proxy, |
| device_info_proxy, |
| system_info_proxy, |
| build_info_proxy, |
| boot_timestamp_nanos, |
| }); |
| } |
| |
| pub async fn identify(&self) -> Result<rcs::IdentifyHostResponse, rcs::IdentifyHostError> { |
| let stream = fnet_interfaces_ext::event_stream_from_state( |
| &self.interface_state_proxy, |
| fnet_interfaces_ext::IncludedAddresses::OnlyAssigned, |
| ) |
| .map_err(|e| { |
| error!(%e, "Getting interface watcher failed"); |
| rcs::IdentifyHostError::ListInterfacesFailed |
| })?; |
| let ilist = fnet_interfaces_ext::existing( |
| stream, |
| HashMap::<u64, fnet_interfaces_ext::PropertiesAndState<()>>::new(), |
| ) |
| .await |
| .map_err(|e| { |
| error!(%e, "Getting existing interfaces failed"); |
| rcs::IdentifyHostError::ListInterfacesFailed |
| })?; |
| |
| let serial_number = 'serial: { |
| match self.system_info_proxy.get_serial_number().await { |
| Ok(Ok(serial)) => break 'serial Some(serial), |
| Ok(Err(status)) => { |
| let status = zx::Status::from_raw(status); |
| warn!(%status, "Failed to get serial from SysInfo") |
| } |
| Err(err) => error!(%err, "SysInfoProxy internal err"), |
| } |
| |
| match self.device_info_proxy.get_info().await { |
| Ok(info) => break 'serial info.serial_number, |
| Err(err) => error!(%err, "DeviceProxy internal err"), |
| } |
| |
| None |
| }; |
| |
| let (product_config, board_config) = self |
| .build_info_proxy |
| .get_build_info() |
| .await |
| .map_err(|e| error!(%e, "buildinfo::ProviderProxy internal err")) |
| .ok() |
| .and_then(|i| Some((i.product_config, i.board_config))) |
| .unwrap_or((None, None)); |
| |
| let addresses = ilist |
| .into_iter() |
| .map(|(_, v): (u64, _)| v) |
| .flat_map(|properties_and_state| { |
| properties_and_state.properties.addresses.into_iter().filter_map( |
| |fnet_interfaces_ext::Address { addr, valid_until: _, assignment_state }| { |
| match assignment_state { |
| fnet_interfaces::AddressAssignmentState::Assigned => Some(addr), |
| fnet_interfaces::AddressAssignmentState::Tentative |
| | fnet_interfaces::AddressAssignmentState::Unavailable => None, |
| } |
| }, |
| ) |
| }) |
| .collect::<Vec<_>>(); |
| |
| let addresses = Some(addresses); |
| |
| let nodename = match self.name_provider_proxy.get_device_name().await { |
| Ok(result) => match result { |
| Ok(name) => Some(name), |
| Err(err) => { |
| error!(%err, "NameProvider internal error"); |
| return Err(rcs::IdentifyHostError::GetDeviceNameFailed); |
| } |
| }, |
| Err(err) => { |
| error!(%err, "Getting nodename failed"); |
| return Err(rcs::IdentifyHostError::GetDeviceNameFailed); |
| } |
| }; |
| |
| let boot_timestamp_nanos = Some(self.boot_timestamp_nanos); |
| |
| Ok(rcs::IdentifyHostResponse { |
| nodename, |
| addresses, |
| serial_number, |
| boot_timestamp_nanos, |
| product_config, |
| board_config, |
| ..Default::default() |
| }) |
| } |
| } |