Skip to main content

remote_control/
host_identifier.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use 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}