1use 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 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 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 #![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 #![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 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}