1use crate::bootup_tracker::BootupTracker;
6use crate::driver_host_runner::DriverHostRunner;
7use crate::memory_attribution::MemoryAttributor;
8use crate::offer_injection::OfferInjector;
9use crate::runner::Runner;
10use crate::{DriverRunnerBridge, LoaderServiceFactory, perform_bfs, to_collection};
11use driver_manager_bind::BindManagerHandle;
12use driver_manager_composite::{CompositeNodeSpec, CompositeNodeSpecManager};
13use driver_manager_devfs::Devfs;
14use driver_manager_driver_host::{DriverHost, DriverHostComponent};
15use driver_manager_node::Node;
16use driver_manager_shutdown::NodeRemovalTracker;
17use driver_manager_types::Collection;
18use driver_manager_utils::DictionaryUtil;
19use fidl::endpoints::{ServerEnd, create_endpoints};
20use fidl_fuchsia_component as fcomponent;
21use fidl_fuchsia_component_decl as fdecl;
22use fidl_fuchsia_component_sandbox as fsandbox;
23use fidl_fuchsia_driver_crash as fcrash;
24use fidl_fuchsia_driver_development as fdd;
25use fidl_fuchsia_driver_framework as fdf;
26use fidl_fuchsia_driver_host as fdh;
27use fidl_fuchsia_driver_index as fdi;
28use fidl_fuchsia_driver_token as fdt;
29use fidl_fuchsia_io as fio;
30use fuchsia_async as fasync;
31use fuchsia_component::client::connect_to_protocol_at_dir_root;
32use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
33use fuchsia_inspect as inspect;
34use fuchsia_inspect::ArrayProperty;
35use futures::StreamExt;
36use futures::channel::oneshot;
37use log::{debug, error, info, warn};
38use rand::rngs::StdRng;
39use std::cell::RefCell;
40use std::collections::HashSet;
41use std::rc::{Rc, Weak};
42use std::sync::Arc;
43use vfs::execution_scope::ExecutionScope;
44
45pub struct DriverRunner {
46 pub(crate) driver_index: fdi::DriverIndexProxy,
47 pub(crate) dictionary_util: Rc<DictionaryUtil>,
48 loader_service_factory: LoaderServiceFactory,
49 pub(crate) root_node: Rc<Node>,
50 pub bind_manager: BindManagerHandle,
51 pub(crate) composite_node_spec_manager: CompositeNodeSpecManager,
52 runner: Runner,
53 driver_host_runner: Rc<DriverHostRunner>,
54 pub(crate) removal_tracker: Rc<RefCell<NodeRemovalTracker>>,
55 pub bootup_tracker: Rc<BootupTracker>,
56 driver_hosts: RefCell<Vec<Weak<dyn DriverHost>>>,
57 pub devfs: Arc<Devfs>,
58 pub(crate) memory_attributor: Rc<MemoryAttributor>,
59 launcher: Option<fidl_fuchsia_driver_loader::DriverHostLauncherProxy>,
60 pub(crate) enable_test_shutdown_delays: bool,
61 pub(crate) shutdown_test_rng: Rc<RefCell<StdRng>>,
62 pub(crate) scope: fasync::Scope,
63}
64
65impl DriverRunner {
66 #[allow(clippy::too_many_arguments)]
67 pub fn new(
68 realm: fcomponent::RealmProxy,
69 introspector: fcomponent::IntrospectorProxy,
70 dictionary_util: DictionaryUtil,
71 driver_index: fdi::DriverIndexProxy,
72 loader_service_factory: LoaderServiceFactory,
73 enable_test_shutdown_delays: bool,
74 offer_injector: OfferInjector,
75 devfs: Arc<Devfs>,
76 ) -> Rc<Self> {
77 Rc::new_cyclic(|weak_driver_runner| {
78 let bind_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
79 let bind_manager = BindManagerHandle::new(bind_manager_bridge);
80
81 let composite_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
82 let composite_node_spec_manager =
83 CompositeNodeSpecManager::new(composite_manager_bridge);
84
85 let bootup_tracker = BootupTracker::new(bind_manager.clone());
86
87 let root_node = Node::new(
88 "dev",
89 Weak::new(),
90 Box::new(DriverRunnerBridge(weak_driver_runner.clone())),
91 );
92 root_node.setup_devfs_for_root_node(devfs.root_node());
93
94 let runner = Runner::new(realm.clone(), introspector, offer_injector);
95 let driver_host_runner = DriverHostRunner::new(realm);
96 let removal_tracker = NodeRemovalTracker::new();
97 let memory_attributor = Rc::new(MemoryAttributor::new());
98
99 Self {
100 driver_index,
101 dictionary_util: Rc::new(dictionary_util),
102 loader_service_factory,
103 root_node,
104 bind_manager,
105 composite_node_spec_manager,
106 runner,
107 driver_host_runner,
108 removal_tracker,
109 bootup_tracker,
110 driver_hosts: RefCell::new(Vec::new()),
111 devfs,
112 memory_attributor,
113 launcher: None,
114 enable_test_shutdown_delays,
115 shutdown_test_rng: Rc::new(RefCell::new(rand::make_rng())),
116 scope: fasync::Scope::new_with_name("driver_runner"),
117 }
118 })
119 }
120
121 pub fn register_notifier(self: &Rc<Self>) -> Result<(), anyhow::Error> {
122 let (client, server) = create_endpoints();
123 self.driver_index.set_notifier(client)?;
124 let weak_self = Rc::downgrade(self);
125 self.scope.spawn_local(async move {
126 let mut stream = server.into_stream();
127 while let Some(Ok(msg)) = stream.next().await {
128 match msg {
129 fdi::DriverNotifierRequest::NewDriverAvailable { .. } => {
130 let Some(this) = weak_self.upgrade() else {
131 return;
132 };
133 let _ = this.bind_manager.try_bind_all_available().await;
134 }
135 }
136 }
137 });
138 Ok(())
139 }
140
141 pub fn root_node(&self) -> Rc<Node> {
142 self.root_node.clone()
143 }
144
145 pub fn get_composite_list_info(&self) -> Vec<fdd::CompositeNodeInfo> {
146 self.composite_node_spec_manager.get_composite_info()
147 }
148
149 pub fn driver_hosts(&self) -> Vec<Rc<dyn DriverHost>> {
150 self.driver_hosts.borrow().iter().filter_map(|w| w.upgrade()).collect()
151 }
152
153 pub fn get_driver_host(
154 &self,
155 driver_host_name_for_colocation: &str,
156 ) -> Option<Rc<dyn DriverHost>> {
157 if driver_host_name_for_colocation.is_empty() {
158 return None;
159 }
160 for host_weak in self.driver_hosts.borrow().iter() {
161 if let Some(host) = host_weak.upgrade()
162 && host.name_for_colocation() == driver_host_name_for_colocation
163 {
164 return Some(host);
165 }
166 }
167 None
168 }
169
170 pub async fn start_root_driver(self: &Rc<Self>, url: String) -> Result<(), zx::Status> {
171 self.bootup_tracker.start();
172 let package_type = if url.starts_with("fuchsia-boot://") {
173 fdf::DriverPackageType::Boot
174 } else {
175 fdf::DriverPackageType::Base
176 };
177
178 let (sender, receiver) = oneshot::channel();
179 self.root_node.set_driver_host_name_for_colocation("root");
180 let self_clone = self.clone();
181 self.scope.spawn_local(async move {
182 if let Err(e) = self_clone.start_driver(&self_clone.root_node, &url, package_type).await
183 {
184 error!("Failed to start root driver: {}", e);
185 sender.send(Err(e)).unwrap();
186 } else {
187 sender.send(Ok(())).unwrap();
188 }
189 });
190 receiver.await.map_err(|_| zx::Status::CANCELED)?
191 }
192
193 pub fn start_devfs_driver(self: &Rc<Self>) {
194 let self_clone = self.clone();
195 self.scope.spawn_local(async move {
196 let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
197 let result = self_clone
198 .runner
199 .create_driver_component(
200 "devfs_driver",
201 "fuchsia-boot:///devfs-driver#meta/devfs-driver.cm",
202 &Collection::Boot.to_string(),
203 &[],
204 None,
205 true,
206 server,
207 )
208 .await;
209 if let Ok((handle, receiver)) = result {
210 self_clone.devfs.set_component_controller_proxy(client);
211 if let Err(e) = self_clone.devfs.attach_component(handle, receiver).await {
212 error!("Failed to attach devfs component: {}", e);
213 }
214 } else {
215 error!("Starting the devfs component failed");
216 }
217 });
218 }
219
220 pub async fn start_driver(
221 self: &Rc<Self>,
222 node: &Rc<Node>,
223 url: &str,
224 package_type: fdf::DriverPackageType,
225 ) -> Result<(), zx::Status> {
226 node.set_collection(to_collection(node, package_type));
235 node.set_driver_package_type(package_type);
236
237 let moniker = node.make_component_moniker();
238 self.bootup_tracker.notify_new_start_request(
239 moniker.clone(),
240 url.to_string(),
241 node.weak_from_this(),
242 );
243
244 match self.start_driver_internal(node, url, &moniker).await {
245 Ok(_) => {
246 node.complete_bind(Ok(())).await;
247 self.bootup_tracker.notify_start_complete(&moniker);
248 Ok(())
249 }
250 Err(err) => {
251 node.on_start_error(err);
252 node.complete_bind(Err(err)).await;
253 self.bootup_tracker.notify_start_complete(&moniker);
254 Err(err)
255 }
256 }
257 }
258
259 async fn start_driver_internal(
260 self: &Rc<Self>,
261 node: &Rc<Node>,
262 url: &str,
263 moniker: &str,
264 ) -> Result<(), zx::Status> {
265 let dictionary = node.prepare_dictionary().await;
266
267 if !node.has_component_controller_proxy() {
268 let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
269
270 let offers = node.offers().clone();
271 let create_result = self
272 .runner
273 .create_driver_component(
274 moniker,
275 url,
276 &node.collection().to_string(),
277 &offers,
278 dictionary,
279 node.skip_injected_offers(),
280 server,
281 )
282 .await;
283
284 match create_result {
285 Ok((handle_info, receiver)) => {
286 node.set_created_info(client, handle_info, receiver).await;
287 }
288 Err(err) => {
289 return Err(err);
290 }
291 }
292 }
293
294 node.send_start_request().await?;
295 let (start_info, controller) = node.get_next_start_request().await?;
296 node.start_driver(start_info, controller).await
297 }
298
299 pub async fn create_driver_host(
300 &self,
301 use_next_vdso: bool,
302 driver_host_name_for_colocation: String,
303 ) -> Result<Rc<dyn DriverHost>, zx::Status> {
304 let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
305 let name = if !driver_host_name_for_colocation.is_empty() {
306 format!("driver-host-{}", driver_host_name_for_colocation.trim_start_matches('#'))
307 } else {
308 format!("driver-host-{}", self.driver_hosts.borrow().len())
309 };
310
311 self.create_driver_host_component(&name, exposed_dir_server, use_next_vdso)?;
312
313 let driver_host_proxy =
314 connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
315 .map_err(|_| zx::Status::INTERNAL)?;
316
317 let (tx, rx) = oneshot::channel();
318 let _ = self.loader_service_factory.unbounded_send(tx);
319 let loader_service_client = rx.await.map_err(|e| {
320 error!("Failed to connect to loader service: {}", e);
321 zx::Status::INTERNAL
322 })??;
323
324 let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
325 driver_host_proxy,
326 None,
327 ExecutionScope::new(),
328 driver_host_name_for_colocation,
329 ));
330 driver_host.install_loader(loader_service_client)?;
331
332 self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
333
334 Ok(driver_host)
335 }
336
337 pub async fn create_driver_host_dynamic_linker(
338 self: &Rc<Self>,
339 driver_host_name_for_colocation: String,
340 ) -> Result<Rc<dyn DriverHost>, zx::Status> {
341 let driver_host_runner = self.driver_host_runner.clone();
342 let launcher = self.launcher.clone().unwrap();
343 let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
344 let loader_client = driver_host_runner
345 .start_driver_host(launcher, exposed_dir_server)
346 .await
347 .map_err(|e| {
348 error!("Failed to start driver host: {e:?}");
349 zx::Status::INTERNAL
350 })?;
351
352 let driver_host_client =
353 connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
354 .map_err(|_| zx::Status::INTERNAL)?;
355 let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
356 driver_host_client,
357 Some(loader_client.into_proxy()),
358 ExecutionScope::new(),
359 driver_host_name_for_colocation,
360 ));
361 self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
362 Ok(driver_host)
363 }
364
365 fn create_driver_host_component(
366 &self,
367 moniker: &str,
368 exposed_dir: ServerEnd<fio::DirectoryMarker>,
369 use_next_vdso: bool,
370 ) -> Result<(), zx::Status> {
371 let url = if use_next_vdso {
372 "fuchsia-boot:///driver_host#meta/driver_host_next.cm"
373 } else {
374 "fuchsia-boot:///driver_host#meta/driver_host.cm"
375 };
376
377 let child_decl = fdecl::Child {
378 name: Some(moniker.to_string()),
379 url: Some(url.to_string()),
380 startup: Some(fdecl::StartupMode::Lazy),
381 ..Default::default()
382 };
383
384 let create_child_args = fcomponent::CreateChildArgs::default();
385
386 let realm = self.runner.realm.clone();
387 let child_moniker = moniker.to_string();
388 self.scope.spawn_local(async move {
389 let result = realm
390 .create_child(
391 &fdecl::CollectionRef { name: "driver-hosts".to_string() },
392 &child_decl,
393 create_child_args,
394 )
395 .await;
396
397 if let Err(e) = result {
398 error!("Failed to create driver host '{}': {}", child_moniker, e);
399 return;
400 }
401
402 let child_ref = fdecl::ChildRef {
403 name: child_moniker.clone(),
404 collection: Some("driver-hosts".to_string()),
405 };
406 let open_result = realm.open_exposed_dir(&child_ref, exposed_dir).await;
407 if let Err(e) = open_result {
408 error!(
409 "Failed to open exposed directory for driver host: '{}': {}",
410 child_moniker, e
411 );
412 }
413 });
414
415 Ok(())
416 }
417
418 pub async fn destroy_driver_host(
419 &self,
420 driver_host_name_for_colocation: String,
421 ) -> Result<(), zx::Status> {
422 let name = if !driver_host_name_for_colocation.is_empty() {
423 let suffix = driver_host_name_for_colocation.trim_start_matches('#');
424 format!("driver-host-{}", suffix)
425 } else {
426 return Err(zx::Status::INVALID_ARGS);
427 };
428
429 let child_ref =
430 fdecl::ChildRef { name: name.clone(), collection: Some("driver-hosts".to_string()) };
431
432 let result = self.runner.realm.destroy_child(&child_ref).await;
433 match result {
434 Ok(Ok(())) => Ok(()),
435 Ok(Err(fcomponent::Error::InstanceNotFound)) => Ok(()),
436 Ok(Err(fcomponent::Error::InstanceDied)) => Ok(()),
437 Ok(Err(e)) => {
438 error!("Failed to destroy driver host '{}': {:?}", name, e);
439 Err(zx::Status::INTERNAL)
440 }
441 Err(e) => {
442 error!("Failed to destroy driver host '{}': {}", name, e);
443 Err(zx::Status::INTERNAL)
444 }
445 }
446 }
447
448 pub fn publish(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'_, ()>>) {
449 self.runner.publish(fs);
450 self.driver_host_runner.publish(fs);
451 self.memory_attributor.publish(fs);
452
453 let this = self.clone();
454 fs.dir("svc").add_fidl_service(move |stream: fdf::CompositeNodeManagerRequestStream| {
455 this.serve_composite_node_manager(stream);
456 });
457
458 let this = self.clone();
459 fs.dir("svc").add_fidl_service(move |stream: fdt::NodeBusTopologyRequestStream| {
460 this.serve_node_bus_topology(stream);
461 });
462
463 let this = self.clone();
464 fs.dir("svc").add_fidl_service(move |stream: fcrash::CrashIntrospectRequestStream| {
465 this.serve_crash_introspect(stream);
466 });
467
468 let this = self.clone();
469 fs.dir("svc").add_fidl_service(move |stream: fdt::DebugRequestStream| {
470 this.serve_debug(stream);
471 });
472 }
473
474 pub fn serve_debug(self: &Rc<Self>, mut stream: fdt::DebugRequestStream) {
475 let this = self.clone();
476 self.scope.spawn_local(async move {
477 while let Some(Ok(request)) = stream.next().await {
478 match request {
479 fdt::DebugRequest::LogStackTrace { node_token, responder } => {
480 let result = this.log_stack_trace(node_token).await;
481 let _ = match result {
482 Ok(()) => responder.send(Ok(())),
483 Err(status) => responder.send(Err(status.into_raw())),
484 };
485 }
486 fdt::DebugRequest::GetHostKoid { node_token, responder } => {
487 let result = this.get_host_koid(node_token).await;
488 let _ = match result {
489 Ok(host_koid) => responder.send(Ok(host_koid.raw_koid())),
490 Err(status) => responder.send(Err(status.into_raw())),
491 };
492 }
493 fdt::DebugRequest::_UnknownMethod { ordinal, .. } => {
494 warn!("Unknown Debug request: {}", ordinal);
495 }
496 }
497 }
498 });
499 }
500
501 async fn log_stack_trace(&self, node_token: zx::Event) -> Result<(), zx::Status> {
502 let token_koid = node_token.basic_info()?.koid;
503 let node = self.find_node_by_token_koid(token_koid).await;
504 if let Some(node) = node {
505 if let Some(host) = node.driver_host() {
506 host.trigger_stack_trace();
507 Ok(())
508 } else {
509 Err(zx::Status::NOT_FOUND)
510 }
511 } else {
512 Err(zx::Status::NOT_FOUND)
513 }
514 }
515
516 async fn get_host_koid(&self, node_token: zx::Event) -> Result<zx::Koid, zx::Status> {
517 let token_koid = node_token.basic_info()?.koid;
518 let node = self.find_node_by_token_koid(token_koid).await;
519 if let Some(node) = node {
520 if let Some(host) = node.driver_host() {
521 host.get_process_koid().await
522 } else {
523 Err(zx::Status::NOT_FOUND)
524 }
525 } else {
526 Err(zx::Status::NOT_FOUND)
527 }
528 }
529
530 pub fn serve_composite_node_manager(
531 self: &Rc<Self>,
532 mut stream: fdf::CompositeNodeManagerRequestStream,
533 ) {
534 let this = self.clone();
535 self.scope.spawn_local(async move {
536 while let Some(Ok(request)) = stream.next().await {
537 this.handle_composite_node_manager_request(request).await;
538 }
539 });
540 }
541
542 async fn handle_composite_node_manager_request(
543 self: &Rc<Self>,
544 request: fdf::CompositeNodeManagerRequest,
545 ) {
546 match request {
547 fdf::CompositeNodeManagerRequest::AddSpec { payload, responder } => {
548 let result = self.add_spec(payload).await;
549 let _ = responder.send(result);
550 }
551 fdf::CompositeNodeManagerRequest::_UnknownMethod { .. } => (),
552 }
553 }
554
555 async fn add_spec(
556 self: &Rc<Self>,
557 spec: fdf::CompositeNodeSpec,
558 ) -> Result<(), fdf::CompositeNodeSpecError> {
559 let name = spec.name.clone().ok_or(fdf::CompositeNodeSpecError::MissingArgs)?;
560
561 if spec.parents2.is_none() {
562 return Err(fdf::CompositeNodeSpecError::MissingArgs);
563 }
564
565 let parents2 = spec.parents2.as_ref().unwrap();
566 if parents2.is_empty() {
567 return Err(fdf::CompositeNodeSpecError::EmptyNodes);
568 }
569 let parents2 = parents2.clone();
570
571 let driver_host_name_for_colocation = spec.driver_host.clone().unwrap_or_default();
572
573 let spec_for_manager = CompositeNodeSpec::new(
574 name,
575 parents2,
576 Box::new(DriverRunnerBridge(Rc::downgrade(self))),
577 driver_host_name_for_colocation,
578 );
579
580 self.composite_node_spec_manager.add_spec(spec, spec_for_manager).await
581 }
582
583 pub fn serve_node_bus_topology(self: &Rc<Self>, mut stream: fdt::NodeBusTopologyRequestStream) {
584 let this = self.clone();
585 self.scope.spawn_local(async move {
586 while let Some(Ok(request)) = stream.next().await {
587 match request {
588 fdt::NodeBusTopologyRequest::Get { token, responder } => {
589 let result = this.get_bus_topology(token).await;
590 let _ = match result {
591 Ok(topology) => responder.send(Ok(&topology)),
592 Err(status) => responder.send(Err(status.into_raw())),
593 };
594 }
595 fdt::NodeBusTopologyRequest::_UnknownMethod { .. } => (),
596 }
597 }
598 });
599 }
600
601 async fn get_bus_topology(&self, token: zx::Event) -> Result<Vec<fdf::BusInfo>, zx::Status> {
602 let token_koid = token.basic_info()?.koid;
603
604 let node = self.find_node_by_token_koid(token_koid).await;
605
606 if let Some(node) = node { Ok(node.get_bus_topology()) } else { Err(zx::Status::NOT_FOUND) }
607 }
608
609 pub fn serve_crash_introspect(
610 self: &Rc<Self>,
611 mut stream: fcrash::CrashIntrospectRequestStream,
612 ) {
613 let this = self.clone();
614 self.scope.spawn_local(async move {
615 while let Some(Ok(request)) = stream.next().await {
616 match request {
617 fcrash::CrashIntrospectRequest::FindDriverCrash {
618 process_koid,
619 thread_koid,
620 responder,
621 } => {
622 let result = this
623 .find_driver_crash(
624 zx::Koid::from_raw(process_koid),
625 zx::Koid::from_raw(thread_koid),
626 )
627 .await;
628 let _ = match result {
629 Ok(info) => responder.send(Ok(&info)),
630 Err(status) => responder.send(Err(status.into_raw())),
631 };
632 }
633 }
634 }
635 });
636 }
637
638 async fn find_driver_crash(
639 &self,
640 process_koid: zx::Koid,
641 thread_koid: zx::Koid,
642 ) -> Result<fcrash::DriverCrashInfo, zx::Status> {
643 let hosts = self.driver_hosts.borrow().clone();
644 for host in hosts {
645 if let Some(host) = host.upgrade()
646 && let Ok(koid) = host.get_process_koid().await
647 && koid == process_koid
648 {
649 let crash_info = host.get_crash_info(thread_koid).await?;
650 let token = crash_info.node_token.ok_or(zx::Status::INTERNAL)?;
651 let token_koid = token.into_handle().basic_info()?.koid;
652
653 let node = self.find_node_by_token_koid(token_koid).await;
654
655 if let Some(node) = node {
656 return Ok(fcrash::DriverCrashInfo {
657 node_moniker: Some(node.make_component_moniker()),
658 url: crash_info.url,
659 ..Default::default()
660 });
661 } else {
662 return Err(zx::Status::NOT_FOUND);
663 }
664 }
665 }
666 Err(zx::Status::NOT_FOUND)
667 }
668
669 async fn find_node_by_token_koid(&self, token_koid: zx::Koid) -> Option<Rc<Node>> {
670 let mut result: Option<Rc<Node>> = None;
671 perform_bfs(self.root_node(), async |current| {
672 if result.is_some() {
673 return false; }
675 if let Some(current_koid) = current.token_koid()
676 && current_koid == token_koid
677 {
678 result = Some(current.clone());
679 return false;
680 }
681 true
682 })
683 .await;
684 result
685 }
686
687 pub async fn rebind_composites_with_driver(&self, driver_url: &str) -> u32 {
688 let mut names = HashSet::new();
689 perform_bfs(self.root_node(), async |node| {
690 if node.is_composite() && node.driver_url() == driver_url {
691 names.insert(node.name().to_string());
692 return false; }
694 true })
696 .await;
697
698 let count = names.len() as u32;
699 for name in names {
700 let _ = self.composite_node_spec_manager.rebind(name, None).await;
701 }
702
703 count
704 }
705
706 pub async fn restart_nodes_colocated_with_driver_url(
707 self: &Rc<Self>,
708 url: &str,
709 rematch_flags: fdd::RestartRematchFlags,
710 ) -> Result<u32, zx::Status> {
711 let mut driver_hosts = HashSet::new();
713 perform_bfs(self.root_node(), async |node| {
714 if node.driver_url() == url
715 && let Some(host) = node.driver_host()
716 {
717 driver_hosts.insert(Rc::as_ptr(&host) as *const ());
720 }
721 true })
723 .await;
724
725 if driver_hosts.is_empty() {
726 warn!(
727 "restart_nodes_colocated_with_driver_url: no driver hosts found with url {}",
728 url
729 );
730 return Ok(0);
731 }
732
733 let driver_host_count = driver_hosts.len() as u32;
734
735 let this = self.clone();
737 perform_bfs(self.root_node(), async |node| {
738 let host_ptr = node.driver_host().map(|host| Rc::as_ptr(&host) as *const ());
739 if host_ptr.is_none() || !driver_hosts.contains(&host_ptr.unwrap()) {
740 return true;
742 }
743
744 if node.evaluate_rematch_flags(rematch_flags, url) {
746 if node.is_composite() {
747 debug!(
748 "RestartNodesColocatedWithDriverUrl rebinding composite {}",
749 node.make_component_moniker()
750 );
751 let _ = this
752 .composite_node_spec_manager
753 .rebind(node.name().to_string(), None)
754 .await;
755 } else {
756 debug!(
757 "RestartNodesColocatedWithDriverUrl restarting node with rematch {}",
758 node.make_component_moniker()
759 );
760 let (tx, _rx) = oneshot::channel();
761 node.restart_node_with_rematch(Some("".to_string()), tx);
762 }
763 } else {
764 info!(
765 "RestartNodesColocatedWithDriverUrl restarting node {}",
766 node.make_component_moniker()
767 );
768 node.restart_node();
769 }
770
771 false })
773 .await;
774
775 Ok(driver_host_count)
776 }
777
778 pub async fn restart_with_dictionary(
779 self: &Rc<Self>,
780 moniker: String,
781 dictionary: fsandbox::DictionaryRef,
782 reset_eventpair: zx::EventPair,
783 ) {
784 let imported = self.dictionary_util.import_dictionary(dictionary).await;
785 let imported = match imported {
786 Ok(imported) => imported,
787 Err(e) => {
788 error!("Failed to import dictionary: {}", e);
789 return;
790 }
791 };
792
793 let mut restarted_node: Option<Rc<Node>> = None;
794 perform_bfs(self.root_node(), async |node| {
795 if restarted_node.is_some() {
796 return false; }
798
799 if node.make_component_moniker() == moniker {
800 if node.has_subtree_dictionary() {
801 error!(concat!(
802 "RestartWithDictionary requested node id already contains a ",
803 "dictionary_ref from another RestartWithDictionary operation."
804 ));
805 return false; }
807 assert!(restarted_node.is_none(), "Multiple nodes with same moniker not possible.");
808 restarted_node = Some(node.clone());
809 node.set_subtree_dictionary(imported);
810 node.restart_node();
811 return false; }
813
814 true })
816 .await;
817
818 if let Some(restarted_node) = restarted_node {
819 self.scope.spawn_local(async move {
820 let signals = zx::Signals::EVENTPAIR_PEER_CLOSED | zx::Signals::EVENTPAIR_SIGNALED;
821 let on_signals = fasync::OnSignals::new(&reset_eventpair, signals);
822 on_signals.await.expect("failed to wait on eventpair");
823
824 info!("RestartWithDictionary operation released.");
825 restarted_node.remove_subtree_dictionary();
826 restarted_node.restart_node();
827 });
828 }
829 }
830
831 pub fn inspect(&self) -> inspect::Inspector {
832 let inspector =
833 inspect::Inspector::new(inspect::InspectorConfig::default().size(2 * 256 * 1024));
834
835 let mut roots = Vec::new();
836 let mut unique_nodes = HashSet::new();
837
838 let device_tree = inspector.root().create_child("node_topology");
839 let mut root_node_inspect = device_tree.create_child(self.root_node.name());
840
841 self.inspect_node_recursive(
842 &self.root_node,
843 &mut root_node_inspect,
844 &mut roots,
845 &mut unique_nodes,
846 );
847
848 device_tree.record(root_node_inspect);
849 inspector.root().record(device_tree);
850
851 for root in roots {
852 inspector.root().record(root);
853 }
854
855 self.bind_manager.record_inspect(inspector.root());
856
857 inspector
858 }
859
860 fn inspect_node_recursive(
861 &self,
862 node: &Rc<Node>,
863 inspect_node: &mut inspect::Node,
864 roots: &mut Vec<inspect::Node>,
865 unique_nodes: &mut HashSet<*const Node>,
866 ) {
867 let node_ptr = Rc::as_ptr(node);
868 if !unique_nodes.insert(node_ptr) {
869 return;
870 }
871
872 let offers = node.offers();
873 if !offers.is_empty() {
874 let array = inspect_node.create_string_array("offers", offers.len());
875 for (i, offer) in offers.iter().enumerate() {
876 array.set(i, &offer.service_name);
877 }
878 inspect_node.record(array);
879 }
880
881 let symbols = node.symbols();
882 if !symbols.is_empty() {
883 let array = inspect_node.create_string_array("symbols", symbols.len());
884 for (i, symbol) in symbols.iter().enumerate() {
885 if let Some(name) = &symbol.name {
886 array.set(i, name);
887 }
888 }
889 inspect_node.record(array);
890 }
891
892 if let Some(properties) = node.get_node_properties(None)
893 && !properties.is_empty()
894 {
895 inspect_node.record_child("properties", |properties_node| {
896 for (i, property) in properties.iter().enumerate() {
897 properties_node.record_child(i.to_string(), |inspect_property| {
898 inspect_property.record_string("key", &property.key);
899 match &property.value {
900 fdf::NodePropertyValue::StringValue(s) => {
901 inspect_property.record_string("value", s)
902 }
903 fdf::NodePropertyValue::IntValue(i) => {
904 inspect_property.record_uint("value", *i as u64)
905 }
906 fdf::NodePropertyValue::EnumValue(e) => {
907 inspect_property.record_string("value", e)
908 }
909 fdf::NodePropertyValue::BoolValue(b) => {
910 inspect_property.record_bool("value", *b)
911 }
912 _ => inspect_property.record_string("value", "UNKNOWN VALUE TYPE"),
913 }
914 });
915 }
916 });
917 }
918
919 inspect_node
920 .record_string("type", if node.is_composite() { "Composite Device" } else { "Device" });
921 inspect_node.record_string("topological_path", node.make_topological_path(false));
922 inspect_node.record_string("driver", node.driver_url());
923
924 for child in node.children() {
925 let mut child_inspect_node = inspect_node.create_child(child.name());
926 self.inspect_node_recursive(&child, &mut child_inspect_node, roots, unique_nodes);
927 roots.push(child_inspect_node);
928 }
929 }
930}
931
932#[cfg(test)]
933mod tests {
934 use super::*;
935 use crate::testing::{MockDriverHost, MockNodeManager};
936 use futures::channel::mpsc;
937 use std::sync::atomic::Ordering;
938
939 use fidl_fuchsia_driver_index as fdi;
940
941 #[fuchsia::test]
942 async fn test_log_stack_trace() {
943 let (realm, _) = fidl::endpoints::create_proxy::<fcomponent::RealmMarker>();
944 let (introspector, _) = fidl::endpoints::create_proxy::<fcomponent::IntrospectorMarker>();
945 let (driver_index, _) = fidl::endpoints::create_proxy::<fdi::DriverIndexMarker>();
946 let (capability_store, _) =
947 fidl::endpoints::create_proxy::<fsandbox::CapabilityStoreMarker>();
948 let (loader_service_factory, _) = mpsc::unbounded();
949
950 let driver_runner = DriverRunner::new(
951 realm,
952 introspector,
953 DictionaryUtil::new(capability_store),
954 driver_index,
955 loader_service_factory,
956 false,
957 OfferInjector::new(crate::offer_injection::PowerOffersConfig {
958 power_inject_offer: false,
959 power_suspend_enabled: false,
960 }),
961 Devfs::new(mpsc::unbounded().0),
962 );
963
964 let node_manager = Box::new(MockNodeManager);
965 let node = Node::new("test_node", Rc::downgrade(&driver_runner.root_node), node_manager);
966 let host = Rc::new(MockDriverHost::new());
967 node.set_host(host.clone());
968
969 let token = zx::Event::create();
972 let token_remote = token.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
973 let koid = token.koid().unwrap();
974
975 node.set_state_for_testing(driver_manager_node::types::NodeState::DriverComponent(
976 driver_manager_node::types::DriverComponent::new(
977 "url".to_string(),
978 token_remote,
979 koid,
980 None,
981 None,
982 None,
983 driver_manager_node::types::DriverState::Running,
984 ),
985 ));
986
987 driver_runner.root_node.add_child_to_children_for_testing(node);
989
990 let result = driver_runner.log_stack_trace(token).await;
992 assert!(result.is_ok());
993 assert_eq!(host.stack_trace_count.load(Ordering::SeqCst), 1);
994
995 let random_token = zx::Event::create();
997 let result = driver_runner.log_stack_trace(random_token).await;
998 assert_eq!(result, Err(zx::Status::NOT_FOUND));
999 }
1000
1001 #[fuchsia::test]
1002 async fn test_get_host_koid() {
1003 let (realm, _) = fidl::endpoints::create_proxy::<fcomponent::RealmMarker>();
1004 let (introspector, _) = fidl::endpoints::create_proxy::<fcomponent::IntrospectorMarker>();
1005 let (driver_index, _) = fidl::endpoints::create_proxy::<fdi::DriverIndexMarker>();
1006 let (capability_store, _) =
1007 fidl::endpoints::create_proxy::<fsandbox::CapabilityStoreMarker>();
1008 let (loader_service_factory, _) = mpsc::unbounded();
1009
1010 let driver_runner = DriverRunner::new(
1011 realm,
1012 introspector,
1013 DictionaryUtil::new(capability_store),
1014 driver_index,
1015 loader_service_factory,
1016 false,
1017 OfferInjector::new(crate::offer_injection::PowerOffersConfig {
1018 power_inject_offer: false,
1019 power_suspend_enabled: false,
1020 }),
1021 Devfs::new(mpsc::unbounded().0),
1022 );
1023
1024 let node_manager = Box::new(MockNodeManager);
1025 let node = Node::new("test_node", Rc::downgrade(&driver_runner.root_node), node_manager);
1026 let host = Rc::new(MockDriverHost::new());
1027 node.set_host(host.clone());
1028
1029 let token = zx::Event::create();
1030 let token_remote = token.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1031 let koid = token.koid().unwrap();
1032
1033 node.set_state_for_testing(driver_manager_node::types::NodeState::DriverComponent(
1034 driver_manager_node::types::DriverComponent::new(
1035 "url".to_string(),
1036 token_remote,
1037 koid,
1038 None,
1039 None,
1040 None,
1041 driver_manager_node::types::DriverState::Running,
1042 ),
1043 ));
1044
1045 driver_runner.root_node.add_child_to_children_for_testing(node);
1046
1047 let result = driver_runner.get_host_koid(token).await;
1048 assert_eq!(result, Ok(zx::Koid::from_raw(0)));
1049
1050 let random_token = zx::Event::create();
1051 let result = driver_runner.get_host_koid(random_token).await;
1052 assert_eq!(result, Err(zx::Status::NOT_FOUND));
1053 }
1054}