remote_control/
host_identifier.rs1use anyhow::{Context as _, Result};
5use log::*;
6use std::collections::HashMap;
7use {
8 fidl_fuchsia_buildinfo as buildinfo, fidl_fuchsia_developer_remotecontrol as rcs,
9 fidl_fuchsia_device as fdevice, fidl_fuchsia_hwinfo as hwinfo,
10 fidl_fuchsia_net_interfaces as fnet_interfaces,
11 fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext, fidl_fuchsia_sysinfo as sysinfo,
12};
13
14#[async_trait::async_trait]
15pub trait Identifier {
16 async fn identify(&self) -> Result<rcs::IdentifyHostResponse, rcs::IdentifyHostError>;
17}
18
19pub struct DefaultIdentifier {
20 pub(crate) boot_timestamp_nanos: u64,
21}
22
23impl DefaultIdentifier {
24 pub fn new() -> Self {
25 let boot_timestamp_nanos = (fuchsia_runtime::utc_time().into_nanos()
26 - zx::MonotonicInstant::get().into_nanos()) as u64;
27 Self { boot_timestamp_nanos }
28 }
29}
30
31#[async_trait::async_trait]
32impl Identifier for DefaultIdentifier {
33 async fn identify(&self) -> Result<rcs::IdentifyHostResponse, rcs::IdentifyHostError> {
34 Ok(rcs::IdentifyHostResponse {
35 nodename: Some("fuchsia-default-nodename".into()),
36 serial_number: Some("fuchsia-default-serial-number".into()),
37 boot_timestamp_nanos: Some(self.boot_timestamp_nanos),
38 ..Default::default()
39 })
40 }
41}
42
43pub struct HostIdentifier {
44 pub(crate) interface_state_proxy: fnet_interfaces::StateProxy,
45 pub(crate) name_provider_proxy: fdevice::NameProviderProxy,
46 pub(crate) device_info_proxy: hwinfo::DeviceProxy,
47 pub(crate) system_info_proxy: sysinfo::SysInfoProxy,
48 pub(crate) build_info_proxy: buildinfo::ProviderProxy,
49 pub(crate) boot_timestamp_nanos: u64,
50 pub(crate) boot_id: u64,
51}
52
53fn connect_to_protocol<P: fidl::endpoints::DiscoverableProtocolMarker>() -> Result<P::Proxy> {
54 fuchsia_component::client::connect_to_protocol::<P>().context(P::DEBUG_NAME)
55}
56
57impl HostIdentifier {
58 pub fn new(boot_id: u64) -> Result<Self> {
59 let interface_state_proxy = connect_to_protocol::<fnet_interfaces::StateMarker>()?;
60 let name_provider_proxy = connect_to_protocol::<fdevice::NameProviderMarker>()?;
61 let device_info_proxy = connect_to_protocol::<hwinfo::DeviceMarker>()?;
62 let system_info_proxy = connect_to_protocol::<sysinfo::SysInfoMarker>()?;
63 let build_info_proxy = connect_to_protocol::<buildinfo::ProviderMarker>()?;
64 let boot_timestamp_nanos =
65 (fuchsia_runtime::utc_time().into_nanos() - zx::BootInstant::get().into_nanos()) as u64;
66 return Ok(Self {
67 interface_state_proxy,
68 name_provider_proxy,
69 device_info_proxy,
70 system_info_proxy,
71 build_info_proxy,
72 boot_timestamp_nanos,
73 boot_id,
74 });
75 }
76}
77
78#[async_trait::async_trait]
79impl Identifier for HostIdentifier {
80 async fn identify(&self) -> Result<rcs::IdentifyHostResponse, rcs::IdentifyHostError> {
81 let stream =
82 fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
83 &self.interface_state_proxy,
84 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
85 )
86 .map_err(|e| {
87 error!(e:%; "Getting interface watcher failed");
88 rcs::IdentifyHostError::ListInterfacesFailed
89 })?;
90 let ilist = fnet_interfaces_ext::existing(
91 stream,
92 HashMap::<u64, fnet_interfaces_ext::PropertiesAndState<(), _>>::new(),
93 )
94 .await
95 .map_err(|e| {
96 error!(e:%; "Getting existing interfaces failed");
97 rcs::IdentifyHostError::ListInterfacesFailed
98 })?;
99
100 let serial_number = 'serial: {
101 match self.system_info_proxy.get_serial_number().await {
102 Ok(Ok(serial)) => break 'serial Some(serial),
103 Ok(Err(status)) => {
104 let status = zx::Status::from_raw(status);
105 warn!(status:%; "Failed to get serial from SysInfo")
106 }
107 Err(err) => error!(err:%; "SysInfoProxy internal err"),
108 }
109
110 match self.device_info_proxy.get_info().await {
111 Ok(info) => break 'serial info.serial_number,
112 Err(err) => error!(err:%; "DeviceProxy internal err"),
113 }
114
115 None
116 };
117
118 let (product_config, board_config) = self
119 .build_info_proxy
120 .get_build_info()
121 .await
122 .map_err(|e| error!(e:%; "buildinfo::ProviderProxy internal err"))
123 .ok()
124 .and_then(|i| Some((i.product_config, i.board_config)))
125 .unwrap_or((None, None));
126
127 let addresses = ilist
128 .into_iter()
129 .map(|(_, v): (u64, _)| v)
130 .flat_map(|properties_and_state| {
131 properties_and_state.properties.addresses.into_iter().filter_map(
132 |fnet_interfaces_ext::Address { addr, assignment_state, .. }| {
133 match assignment_state {
134 fnet_interfaces::AddressAssignmentState::Assigned => Some(addr),
135 fnet_interfaces::AddressAssignmentState::Tentative
136 | fnet_interfaces::AddressAssignmentState::Unavailable => None,
137 }
138 },
139 )
140 })
141 .collect::<Vec<_>>();
142
143 let addresses = Some(addresses);
144
145 let nodename = match self.name_provider_proxy.get_device_name().await {
146 Ok(result) => match result {
147 Ok(name) => Some(name),
148 Err(err) => {
149 error!(err:%; "NameProvider internal error");
150 return Err(rcs::IdentifyHostError::GetDeviceNameFailed);
151 }
152 },
153 Err(err) => {
154 error!(err:%; "Getting nodename failed");
155 return Err(rcs::IdentifyHostError::GetDeviceNameFailed);
156 }
157 };
158
159 let boot_timestamp_nanos = Some(self.boot_timestamp_nanos);
160
161 let boot_id = Some(self.boot_id);
162
163 Ok(rcs::IdentifyHostResponse {
164 nodename,
165 addresses,
166 serial_number,
167 boot_timestamp_nanos,
168 product_config,
169 board_config,
170 boot_id,
171 ..Default::default()
172 })
173 }
174}