| // Copyright 2020 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 { |
| crate::{builtin::capability::BuiltinCapability, capability::InternalCapability}, |
| anyhow::{anyhow, Context, Error}, |
| async_trait::async_trait, |
| cm_rust::CapabilityName, |
| fidl_fuchsia_time as ftime, |
| fuchsia_zircon::{Clock, ClockOpts, HandleBased, Rights, Time}, |
| futures::prelude::*, |
| io_util::{file, OPEN_RIGHT_READABLE}, |
| lazy_static::lazy_static, |
| std::sync::Arc, |
| }; |
| |
| lazy_static! { |
| static ref TIME_MAINTENANCE_CAPABILITY_NAME: CapabilityName = "fuchsia.time.Maintenance".into(); |
| } |
| |
| /// An implementation of the `fuchsia.time.Maintenance` protocol, which |
| /// maintains a UTC clock, vending out handles with write access. |
| /// Consumers of this protocol are meant to keep the clock synchronized |
| /// with external time sources. |
| pub struct UtcTimeMaintainer { |
| utc_clock: Arc<Clock>, |
| } |
| |
| impl UtcTimeMaintainer { |
| pub fn new(utc_clock: Arc<Clock>) -> Self { |
| UtcTimeMaintainer { utc_clock } |
| } |
| } |
| |
| #[async_trait] |
| impl BuiltinCapability for UtcTimeMaintainer { |
| const NAME: &'static str = "TimeMaintenance"; |
| type Marker = ftime::MaintenanceMarker; |
| |
| async fn serve( |
| self: Arc<Self>, |
| mut stream: ftime::MaintenanceRequestStream, |
| ) -> Result<(), Error> { |
| while let Some(ftime::MaintenanceRequest::GetWritableUtcClock { responder }) = |
| stream.try_next().await? |
| { |
| responder.send(self.utc_clock.duplicate_handle(Rights::SAME_RIGHTS)?)?; |
| } |
| Ok(()) |
| } |
| |
| fn matches_routed_capability(&self, capability: &InternalCapability) -> bool { |
| capability.matches_protocol(&TIME_MAINTENANCE_CAPABILITY_NAME) |
| } |
| } |
| |
| async fn read_utc_backstop(path: &str) -> Result<Time, Error> { |
| let file_proxy = file::open_in_namespace(path, OPEN_RIGHT_READABLE) |
| .context("failed to open backstop time file from disk")?; |
| let file_contents = file::read_to_string(&file_proxy) |
| .await |
| .context("failed to read backstop time from disk")?; |
| let parsed_time = |
| file_contents.trim().parse::<i64>().context("failed to parse backstop time")?; |
| Ok(Time::from_nanos( |
| parsed_time |
| .checked_mul(1_000_000_000) |
| .ok_or_else(|| anyhow!("backstop time is too large"))?, |
| )) |
| } |
| |
| /// Creates a UTC kernel clock with a backstop time configured by /boot. |
| pub async fn create_utc_clock() -> Result<Clock, Error> { |
| let backstop = read_utc_backstop("/boot/config/build_info/minimum_utc_stamp").await?; |
| let clock = Clock::create(ClockOpts::empty(), Some(backstop)) |
| .map_err(|s| anyhow!("failed to create UTC clock: {}", s))?; |
| Ok(clock) |
| } |