Skip to main content

driver_manager_shutdown/
shutdown_manager.rs

1// Copyright 2026 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 crate::node_remover::NodeRemover;
6use fidl_fuchsia_diagnostics as fdiagnostics;
7use fidl_fuchsia_kernel as fkernel;
8use fidl_fuchsia_process_lifecycle as flifecycle;
9use fidl_fuchsia_system_state as fsystem_state;
10use fuchsia_async as fasync;
11use fuchsia_component::client::{connect_to_protocol, connect_to_protocol_sync};
12use fuchsia_component::server::{FidlService, ServiceFs, ServiceObjLocal};
13use futures::channel::oneshot;
14use futures::prelude::*;
15use log::{error, info, warn};
16use std::cell::RefCell;
17use std::rc::Rc;
18use zx::sys::{
19    ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT, ZX_SYSTEM_POWERCTL_REBOOT,
20    ZX_SYSTEM_POWERCTL_REBOOT_BOOTLOADER, ZX_SYSTEM_POWERCTL_REBOOT_RECOVERY,
21    ZX_SYSTEM_POWERCTL_SHUTDOWN,
22};
23
24#[derive(Copy, Clone, PartialEq, Debug)]
25enum State {
26    Running,
27    PackageStopping,
28    PackageStopped,
29    BootStopping,
30    Stopped,
31}
32
33type ShutdownSender = oneshot::Sender<Result<(), zx::Status>>;
34
35struct LifecycleServer {
36    on_stop: RefCell<Option<oneshot::Sender<ShutdownSender>>>,
37}
38
39impl LifecycleServer {
40    fn new(on_stop: oneshot::Sender<ShutdownSender>) -> Self {
41        Self { on_stop: RefCell::new(Some(on_stop)) }
42    }
43
44    async fn serve(
45        self: Rc<Self>,
46        mut stream: flifecycle::LifecycleRequestStream,
47    ) -> Result<(), fidl::Error> {
48        if let Some(request) = stream.try_next().await? {
49            match request {
50                flifecycle::LifecycleRequest::Stop { control_handle } => {
51                    let (tx, rx) = oneshot::channel();
52                    let on_stop = self.on_stop.borrow_mut().take();
53                    if let Some(on_stop) = on_stop {
54                        let _ = on_stop.send(tx);
55                        if let Ok(result) = rx.await {
56                            control_handle.shutdown_with_epitaph(result);
57                        } else {
58                            control_handle.shutdown_with_epitaph(zx::Status::INTERNAL);
59                        }
60                    }
61                }
62            }
63        }
64        Ok(())
65    }
66}
67
68struct ShutdownManagerState {
69    state: State,
70    received_boot_shutdown_signal: bool,
71    package_shutdown_complete_callbacks: Vec<ShutdownSender>,
72    boot_shutdown_complete_callbacks: Vec<ShutdownSender>,
73    lifecycle_stop: bool,
74}
75
76pub struct ShutdownManager {
77    node_remover: Rc<dyn NodeRemover>,
78    power_resource: Option<zx::Resource>,
79    mexec_resource: Option<zx::Resource>,
80    log_flush: Option<fdiagnostics::LogFlusherProxy>,
81    internal_state: RefCell<ShutdownManagerState>,
82    scope: fasync::Scope,
83}
84
85fn get_power_resource() -> Result<zx::Resource, anyhow::Error> {
86    let client = connect_to_protocol_sync::<fkernel::PowerResourceMarker>()?;
87    let resource = client.get(zx::MonotonicInstant::INFINITE)?;
88    Ok(resource)
89}
90
91fn get_mexec_resource() -> Result<zx::Resource, anyhow::Error> {
92    let client = connect_to_protocol_sync::<fkernel::MexecResourceMarker>()?;
93    let resource = client.get(zx::MonotonicInstant::INFINITE)?;
94    Ok(resource)
95}
96
97async fn get_system_power_state() -> fsystem_state::SystemPowerState {
98    let client = match connect_to_protocol::<fsystem_state::SystemStateTransitionMarker>() {
99        Ok(c) => c,
100        Err(e) => {
101            error!("Failed to connect to StateStateTransition: {}, falling back to default", e);
102            return fsystem_state::SystemPowerState::Reboot;
103        }
104    };
105
106    match client.get_termination_system_state().await {
107        Ok(state) => state,
108        Err(e) => {
109            error!("Failed to get termination system state: {}, falling back to default", e);
110            fsystem_state::SystemPowerState::Reboot
111        }
112    }
113}
114
115impl ShutdownManager {
116    pub fn new(node_remover: Rc<dyn NodeRemover>) -> Rc<Self> {
117        let power_resource = get_power_resource()
118            .inspect_err(|e| {
119                info!("Failed to get power resource, assuming test environment: {}", e)
120            })
121            .ok();
122        let mexec_resource = get_mexec_resource()
123            .inspect_err(|e| {
124                info!("Failed to get mexec resource, assuming test environment: {}", e)
125            })
126            .ok();
127        let log_flush = connect_to_protocol::<fdiagnostics::LogFlusherMarker>()
128            .inspect_err(|e| error!("Failed to connect to LogFlusher: {}", e))
129            .ok();
130
131        let shutdown_manager = Rc::new(Self {
132            node_remover: node_remover.clone(),
133            internal_state: RefCell::new(ShutdownManagerState {
134                state: State::Running,
135                received_boot_shutdown_signal: false,
136                package_shutdown_complete_callbacks: Vec::new(),
137                boot_shutdown_complete_callbacks: Vec::new(),
138                lifecycle_stop: false,
139            }),
140            power_resource,
141            mexec_resource,
142            log_flush,
143            scope: fasync::Scope::new_with_name("shutdown_manager"),
144        });
145
146        let weak_manager = Rc::downgrade(&shutdown_manager);
147        node_remover.set_on_removal_timeout_callback(Box::new(move || {
148            if let Some(strong_manager) = weak_manager.upgrade() {
149                info!("Driver timed out during shutdown, issuing syscall to reboot/shutdown");
150                let strong_manager_clone = strong_manager.clone();
151                strong_manager.scope.spawn_local(async move {
152                    strong_manager_clone.system_execute().await;
153                });
154            }
155        }));
156
157        shutdown_manager
158    }
159
160    pub fn publish<'a>(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'a, ()>>) {
161        let self_clone = self.clone();
162        let (tx, rx) = oneshot::channel::<ShutdownSender>();
163        self.scope.spawn_local(async move {
164            if let Ok(sender) = rx.await {
165                let status = self_clone.signal_package_shutdown().await;
166                let _ = sender.send(status);
167            }
168        });
169        let devfs_with_pkg_lifecycle = Rc::new(LifecycleServer::new(tx));
170
171        let scope = self.scope.as_handle().clone();
172        fs.dir("svc").add_service_at(
173            "fuchsia.device.fs.with.pkg.lifecycle.Lifecycle",
174            FidlService::from(move |stream: flifecycle::LifecycleRequestStream| {
175                let devfs_with_pkg_lifecycle = devfs_with_pkg_lifecycle.clone();
176                scope.spawn_local(async move {
177                    devfs_with_pkg_lifecycle.serve(stream).await.unwrap_or_else(|e| {
178                        error!("Failed to serve devfs with pkg lifecycle: {}", e)
179                    });
180                });
181            }),
182        );
183
184        let self_clone = self.clone();
185        let (tx, rx) = oneshot::channel::<ShutdownSender>();
186        self.scope.spawn_local(async move {
187            if let Ok(sender) = rx.await {
188                let status = self_clone.signal_boot_shutdown().await;
189                let _ = sender.send(status);
190            }
191        });
192        let devfs_lifecycle = Rc::new(LifecycleServer::new(tx));
193
194        let scope = self.scope.as_handle().clone();
195        fs.dir("svc").add_service_at(
196            "fuchsia.device.fs.lifecycle.Lifecycle",
197            FidlService::from(move |stream: flifecycle::LifecycleRequestStream| {
198                let devfs_lifecycle = devfs_lifecycle.clone();
199                scope.spawn_local(async move {
200                    devfs_lifecycle
201                        .serve(stream)
202                        .await
203                        .unwrap_or_else(|e| error!("Failed to serve devfs lifecycle: {}", e));
204                });
205            }),
206        );
207
208        // Bind to process lifecycle
209        let self_clone = self.clone();
210        let (tx, rx) = oneshot::channel::<ShutdownSender>();
211        self.scope.spawn_local(async move {
212            if let Ok(sender) = rx.await {
213                self_clone.internal_state.borrow_mut().lifecycle_stop = true;
214                let status = self_clone.signal_boot_shutdown().await;
215                let _ = sender.send(status);
216            }
217        });
218        let lifecycle_server = Rc::new(LifecycleServer::new(tx));
219
220        if let Some(handle) =
221            fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleType::Lifecycle.into())
222        {
223            let channel = zx::Channel::from(handle);
224            let server_end =
225                fidl::endpoints::ServerEnd::<flifecycle::LifecycleMarker>::new(channel);
226            let stream = server_end.into_stream();
227
228            let self_clone = self.clone();
229            self.scope.spawn_local(async move {
230                if let Err(e) = lifecycle_server.serve(stream).await {
231                    error!("Lifecycle connection got unbound: {}", e);
232                    // Per C++ implementation, we should shut down if this happens.
233                    let _ = self_clone.signal_boot_shutdown().await;
234                }
235            });
236        } else {
237            info!(concat!(
238                "No valid handle found for lifecycle events, assuming test environment ",
239                "and continuing"
240            ));
241        }
242    }
243
244    async fn on_package_shutdown_complete(&self) {
245        info!("Package shutdown complete");
246        let received_boot_shutdown_signal = {
247            let mut internal_state = self.internal_state.borrow_mut();
248            assert_eq!(internal_state.state, State::PackageStopping);
249            internal_state.state = State::PackageStopped;
250
251            for sender in internal_state.package_shutdown_complete_callbacks.drain(..) {
252                let _ = sender.send(Ok(()));
253            }
254
255            if internal_state.received_boot_shutdown_signal {
256                internal_state.state = State::BootStopping;
257                true
258            } else {
259                false
260            }
261        };
262
263        if received_boot_shutdown_signal {
264            self.node_remover.shutdown_all_drivers().await;
265            self.on_boot_shutdown_complete().await;
266        }
267    }
268
269    async fn on_boot_shutdown_complete(&self) {
270        {
271            let mut internal_state = self.internal_state.borrow_mut();
272            assert_eq!(internal_state.state, State::BootStopping);
273            internal_state.state = State::Stopped;
274        }
275        self.system_execute().await;
276        let mut internal_state = self.internal_state.borrow_mut();
277        for sender in internal_state.boot_shutdown_complete_callbacks.drain(..) {
278            let _ = sender.send(Ok(()));
279        }
280    }
281
282    async fn signal_package_shutdown(&self) -> Result<(), zx::Status> {
283        // TODO: switch logs to debuglog
284
285        // We explicitly drop this before going into the await.
286        #![allow(clippy::await_holding_refcell_ref)]
287        let mut internal_state = self.internal_state.borrow_mut();
288
289        match internal_state.state {
290            State::Running | State::PackageStopping => {
291                let (tx, rx) = oneshot::channel();
292                internal_state.package_shutdown_complete_callbacks.push(tx);
293                if internal_state.state == State::Running {
294                    internal_state.state = State::PackageStopping;
295                    drop(internal_state);
296                    self.node_remover.shutdown_pkg_drivers().await;
297                    self.on_package_shutdown_complete().await;
298                } else {
299                    drop(internal_state);
300                }
301                rx.await.unwrap_or(Err(zx::Status::INTERNAL))
302            }
303            _ => Ok(()),
304        }
305    }
306
307    async fn signal_boot_shutdown(&self) -> Result<(), zx::Status> {
308        // We explicitly drop this before going into the await.
309        #![allow(clippy::await_holding_refcell_ref)]
310        let mut internal_state = self.internal_state.borrow_mut();
311
312        if internal_state.state == State::Stopped {
313            return Ok(());
314        }
315
316        let (tx, rx) = oneshot::channel();
317        internal_state.boot_shutdown_complete_callbacks.push(tx);
318
319        internal_state.received_boot_shutdown_signal = true;
320        let state = internal_state.state;
321        match state {
322            State::Running | State::PackageStopped => {
323                internal_state.state = State::BootStopping;
324                drop(internal_state);
325
326                self.node_remover.shutdown_all_drivers().await;
327                self.on_boot_shutdown_complete().await;
328            }
329            State::BootStopping => {
330                error!("SignalBootShutdown() called during shutdown.");
331            }
332            _ => {}
333        }
334        rx.await.unwrap_or(Err(zx::Status::INTERNAL))
335    }
336
337    async fn system_execute(&self) {
338        let shutdown_system_state = get_system_power_state().await;
339        info!("Suspend fallback with flags {:?}", shutdown_system_state);
340        let mut what = "zx_system_powerctl";
341
342        let (Some(mexec_resource), Some(power_resource)) =
343            (&self.mexec_resource, &self.power_resource)
344        else {
345            warn!("Invalid Power/mexec resources. Assuming test.");
346            let internal_state = self.internal_state.borrow();
347            if internal_state.lifecycle_stop {
348                info!("Exiting driver manager gracefully");
349                std::process::exit(0);
350            }
351            return;
352        };
353
354        info!("Flushing logs.");
355        if let Some(log_flush) = &self.log_flush
356            && let Err(e) = log_flush.wait_until_flushed().await
357        {
358            warn!("Failed to flush logs: {}", e);
359        }
360
361        info!("Executing powerctl.");
362        let status = match shutdown_system_state {
363            fsystem_state::SystemPowerState::Reboot => zx::Status::ok(unsafe {
364                zx::sys::zx_system_powerctl(
365                    power_resource.raw_handle(),
366                    ZX_SYSTEM_POWERCTL_REBOOT,
367                    std::ptr::null(),
368                )
369            }),
370            fsystem_state::SystemPowerState::RebootBootloader => zx::Status::ok(unsafe {
371                zx::sys::zx_system_powerctl(
372                    power_resource.raw_handle(),
373                    ZX_SYSTEM_POWERCTL_REBOOT_BOOTLOADER,
374                    std::ptr::null(),
375                )
376            }),
377            fsystem_state::SystemPowerState::RebootRecovery => zx::Status::ok(unsafe {
378                zx::sys::zx_system_powerctl(
379                    power_resource.raw_handle(),
380                    ZX_SYSTEM_POWERCTL_REBOOT_RECOVERY,
381                    std::ptr::null(),
382                )
383            }),
384            fsystem_state::SystemPowerState::RebootKernelInitiated => {
385                let status = zx::Status::ok(unsafe {
386                    zx::sys::zx_system_powerctl(
387                        power_resource.raw_handle(),
388                        ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT,
389                        std::ptr::null(),
390                    )
391                });
392                if status.is_ok() {
393                    // sleep indefinitely
394                    loop {
395                        fasync::Timer::new(std::time::Duration::from_secs(5 * 60)).await;
396                        println!(
397                            "driver_manager: unexpectedly still running after successful reboot syscall"
398                        );
399                    }
400                }
401                status
402            }
403            fsystem_state::SystemPowerState::Poweroff => zx::Status::ok(unsafe {
404                zx::sys::zx_system_powerctl(
405                    power_resource.raw_handle(),
406                    ZX_SYSTEM_POWERCTL_SHUTDOWN,
407                    std::ptr::null(),
408                )
409            }),
410
411            fsystem_state::SystemPowerState::Mexec => {
412                info!("About to mexec...");
413                match mexec_boot::mexec_boot(zx::Unowned::new(mexec_resource)) {
414                    Ok(()) => Ok(()),
415                    Err(e) => {
416                        error!("mexec_boot failed: {}", e);
417                        what = "zx_system_mexec";
418                        Err(zx::Status::INTERNAL)
419                    }
420                }
421            }
422            fsystem_state::SystemPowerState::FullyOn
423            | fsystem_state::SystemPowerState::SuspendRam => {
424                error!("Unexpected shutdown state requested: {:?}", shutdown_system_state);
425                Err(zx::Status::INVALID_ARGS)
426            }
427        };
428
429        let internal_state = self.internal_state.borrow();
430        if internal_state.lifecycle_stop {
431            info!("Exiting driver manager gracefully");
432            std::process::exit(0);
433        }
434
435        warn!("{}: {status:?}", what);
436    }
437}