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