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