system_update_committer/
reboot.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.
4
5use anyhow::{Context, anyhow};
6use fidl_fuchsia_hardware_power_statecontrol::{
7    AdminProxy as PowerStateControlProxy, ShutdownAction, ShutdownOptions, ShutdownReason,
8};
9use fuchsia_async as fasync;
10use log::error;
11use zx::Status;
12
13/// Waits for a timer to fire and then reboots the system, logging errors instead of failing.
14pub(super) async fn wait_and_reboot(timer: fasync::Timer, proxy: &PowerStateControlProxy) {
15    let () = timer.await;
16    if let Err(e) = async move {
17        proxy
18            .shutdown(&ShutdownOptions {
19                action: Some(ShutdownAction::Reboot),
20                reasons: Some(vec![ShutdownReason::RetrySystemUpdate]),
21                ..Default::default()
22            })
23            .await
24            .context("while performing reboot call")?
25            .map_err(Status::from_raw)
26            .context("reboot responded with")
27    }
28    .await
29    {
30        error!("error initiating reboot: {:#}", anyhow!(e));
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use fidl_fuchsia_hardware_power_statecontrol::{
38        ShutdownAction, ShutdownOptions, ShutdownReason,
39    };
40    use fuchsia_sync::Mutex;
41    use futures::channel::oneshot;
42    use futures::pin_mut;
43    use futures::task::Poll;
44    use mock_reboot::MockRebootService;
45    use std::sync::Arc;
46    use std::time::Duration;
47
48    #[test]
49    fn test_wait_and_reboot_success() {
50        let mut executor = fasync::TestExecutor::new_with_fake_time();
51
52        // Create a mock reboot service.
53        let (sender, recv) = oneshot::channel();
54        let sender = Arc::new(Mutex::new(Some(sender)));
55        let mock = Arc::new(MockRebootService::new(Box::new(move |options: ShutdownOptions| {
56            sender.lock().take().unwrap().send(options).unwrap();
57            Ok(())
58        })));
59        let proxy = mock.spawn_reboot_service();
60
61        // Prepare futures to call reboot and receive the reboot request.
62        let timer_duration = 5;
63        let reboot_fut =
64            wait_and_reboot(fasync::Timer::new(Duration::from_secs(timer_duration)), &proxy);
65        pin_mut!(reboot_fut);
66        pin_mut!(recv);
67
68        // Set the time so that the timer is still going, so we should neither call reboot nor
69        // observe the reboot service was called.
70        executor.set_fake_time(fasync::MonotonicInstant::after(
71            Duration::from_secs(timer_duration - 1).into(),
72        ));
73        assert!(!executor.wake_expired_timers());
74        match executor.run_until_stalled(&mut reboot_fut) {
75            Poll::Ready(res) => panic!("future unexpectedly completed with response: {res:?}"),
76            Poll::Pending => {}
77        };
78        match executor.run_until_stalled(&mut recv) {
79            Poll::Ready(res) => panic!("future unexpectedly completed with response: {res:?}"),
80            Poll::Pending => {}
81        };
82
83        // Once the timer completes, we should complete the reboot call and observe we called the
84        // reboot service with the given reboot reason.
85        executor.set_fake_time(fasync::MonotonicInstant::after(Duration::from_secs(1).into()));
86        assert!(executor.wake_expired_timers());
87        match executor.run_until_stalled(&mut recv) {
88            Poll::Ready(res) => panic!("future unexpectedly completed with response: {res:?}"),
89            Poll::Pending => {}
90        };
91        match executor.run_until_stalled(&mut reboot_fut) {
92            Poll::Ready(_) => {}
93            Poll::Pending => panic!("future unexpectedly pending"),
94        };
95        match executor.run_until_stalled(&mut recv) {
96            Poll::Ready(res) => assert_eq!(
97                res,
98                Ok(ShutdownOptions {
99                    action: Some(ShutdownAction::Reboot),
100                    reasons: Some(vec![ShutdownReason::RetrySystemUpdate]),
101                    ..Default::default()
102                })
103            ),
104            Poll::Pending => panic!("future unexpectedly pending"),
105        };
106    }
107}