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