blob: 532074edeabfebc377e10aa57fcb1d134c28d15c [file] [log] [blame]
// 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 {
anyhow::anyhow,
fidl_fuchsia_update::CommitStatusProviderProxy,
fuchsia_zircon::{self as zx, AsHandleRef},
};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum CommitStatus {
Pending,
Committed,
}
/// Queries the commit status using `provider`.
pub async fn query_commit_status(
provider: &CommitStatusProviderProxy,
) -> Result<CommitStatus, anyhow::Error> {
let event_pair = provider.is_current_system_committed().await.unwrap_or_else(|e| {
// TODO(fxbug.dev/66760): remove this once we get critical components support in v2.
// A FIDL error probably indicates the CommitStatusProvider crashed. In order to prevent
// OTAs from being indefinitely blocked, we crash here to force the system to reboot (since
// update checker is a critical component). Ideally, we'd implement this in the component
// serving CommitStatusProvider. However, since that component is v2, and v2 components do
// not have watchdog support, for now we'll make a "DIY watchdog" here to ensure the system
// reboots. We can remove this once there's a v2 equivalent to critical components.
//
// In practice, if the component serving CommitStatusProvider crashes, the component
// manager should restart the crashed component once we try to reconnect to
// CommitStatusProvider. We probably want to do a full reboot when the component serving
// CommitStatusProvider crashes, but the current behavior is acceptable for now.
panic!("got error with is_current_system_committed(), crashing: {:#}", anyhow!(e));
});
match event_pair.wait_handle(zx::Signals::USER_0, zx::Time::INFINITE_PAST) {
Ok(_) => Ok(CommitStatus::Committed),
Err(zx::Status::TIMED_OUT) => Ok(CommitStatus::Pending),
Err(status) => Err(anyhow!("unexpected status while asserting signal: {:?}", status)),
}
}
#[cfg(test)]
mod tests {
use {
super::*,
fidl::endpoints::create_proxy_and_stream,
fidl_fuchsia_update::{CommitStatusProviderMarker, CommitStatusProviderRequest},
fuchsia_async::{self as fasync, futures::StreamExt},
fuchsia_zircon::{HandleBased, Peered},
};
// Verifies that query_commit_status returns the expected CommitStatus.
#[fasync::run_singlethreaded(test)]
async fn test_query_commit_status() {
let (proxy, mut stream) = create_proxy_and_stream::<CommitStatusProviderMarker>().unwrap();
let (p0, p1) = zx::EventPair::create().unwrap();
let _fidl_server = fasync::Task::local(async move {
while let Some(Ok(req)) = stream.next().await {
let CommitStatusProviderRequest::IsCurrentSystemCommitted { responder } = req;
let () = responder.send(p1.duplicate_handle(zx::Rights::BASIC).unwrap()).unwrap();
}
});
// When no signals are asserted, we should report Pending.
assert_eq!(query_commit_status(&proxy).await.unwrap(), CommitStatus::Pending);
// When USER_0 is asserted, we should report Committed.
let () = p0.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
assert_eq!(query_commit_status(&proxy).await.unwrap(), CommitStatus::Committed,);
}
// Verifies we panic when the FIDL call in query_commit_status fails.
// TODO(fxbug.dev/66760): fold this into `test_query_commit_status` since it shouldn't panic.
#[fasync::run_singlethreaded(test)]
#[should_panic]
async fn test_query_commit_status_panic() {
let (proxy, stream) = create_proxy_and_stream::<CommitStatusProviderMarker>().unwrap();
drop(stream);
let _ = query_commit_status(&proxy).await;
}
}