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