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 = fnet_interfaces_ext::event_stream_from_state::<
82 fnet_interfaces_ext::DefaultInterest,
83 >(&self.interface_state_proxy, Default::default())
84 .map_err(|e| {
85 error!(e:%; "Getting interface watcher failed");
86 rcs::IdentifyHostError::ListInterfacesFailed
87 })?;
88 let ilist = fnet_interfaces_ext::existing(
89 stream,
90 HashMap::<u64, fnet_interfaces_ext::PropertiesAndState<(), _>>::new(),
91 )
92 .await
93 .map_err(|e| {
94 error!(e:%; "Getting existing interfaces failed");
95 rcs::IdentifyHostError::ListInterfacesFailed
96 })?;
97
98 let serial_number = 'serial: {
99 match self.system_info_proxy.get_serial_number().await {
100 Ok(Ok(serial)) => break 'serial Some(serial),
101 Ok(Err(status)) => {
102 let status = zx::Status::from_raw(status);
103 warn!(status:%; "Failed to get serial from SysInfo")
104 }
105 Err(err) => error!(err:%; "SysInfoProxy internal err"),
106 }
107
108 match self.device_info_proxy.get_info().await {
109 Ok(info) => break 'serial info.serial_number,
110 Err(err) => error!(err:%; "DeviceProxy internal err"),
111 }
112
113 None
114 };
115
116 let (product_config, board_config) = self
117 .build_info_proxy
118 .get_build_info()
119 .await
120 .map_err(|e| error!(e:%; "buildinfo::ProviderProxy internal err"))
121 .ok()
122 .and_then(|i| Some((i.product_config, i.board_config)))
123 .unwrap_or((None, None));
124
125 let addresses = ilist
126 .into_iter()
127 .map(|(_, v): (u64, _)| v)
128 .flat_map(|properties_and_state| {
129 properties_and_state.properties.addresses.into_iter().filter_map(
130 |fnet_interfaces_ext::Address { addr, assignment_state, .. }| {
131 match assignment_state {
132 fnet_interfaces::AddressAssignmentState::Assigned => Some(addr),
133 fnet_interfaces::AddressAssignmentState::Tentative
134 | fnet_interfaces::AddressAssignmentState::Unavailable => None,
135 }
136 },
137 )
138 })
139 .collect::<Vec<_>>();
140
141 let addresses = Some(addresses);
142
143 let nodename = match self.name_provider_proxy.get_device_name().await {
144 Ok(result) => match result {
145 Ok(name) => Some(name),
146 Err(err) => {
147 error!(err:%; "NameProvider internal error");
148 return Err(rcs::IdentifyHostError::GetDeviceNameFailed);
149 }
150 },
151 Err(err) => {
152 error!(err:%; "Getting nodename failed");
153 return Err(rcs::IdentifyHostError::GetDeviceNameFailed);
154 }
155 };
156
157 let boot_timestamp_nanos = Some(self.boot_timestamp_nanos);
158
159 let boot_id = Some(self.boot_id);
160
161 Ok(rcs::IdentifyHostResponse {
162 nodename,
163 addresses,
164 serial_number,
165 boot_timestamp_nanos,
166 product_config,
167 board_config,
168 boot_id,
169 ..Default::default()
170 })
171 }
172}