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, to_bind_rule2, to_property2};
18use driver_manager_utils::DictionaryUtil;
19use fidl::endpoints::{ServerEnd, create_endpoints};
20use fuchsia_component::client::connect_to_protocol_at_dir_root;
21use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
22use fuchsia_inspect::ArrayProperty;
23use futures::StreamExt;
24use futures::channel::oneshot;
25use log::{debug, error, info, warn};
26use rand::SeedableRng;
27use rand::rngs::StdRng;
28use std::cell::RefCell;
29use std::collections::HashSet;
30use std::rc::{Rc, Weak};
31use std::sync::Arc;
32use vfs::execution_scope::ExecutionScope;
33use {
34 fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
35 fidl_fuchsia_component_sandbox as fsandbox, fidl_fuchsia_driver_crash as fcrash,
36 fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_framework as fdf,
37 fidl_fuchsia_driver_host as fdh, fidl_fuchsia_driver_index as fdi,
38 fidl_fuchsia_driver_token as fdt, fidl_fuchsia_io as fio, fuchsia_async as fasync,
39 fuchsia_inspect as inspect,
40};
41
42pub struct DriverRunner {
43 pub(crate) driver_index: fdi::DriverIndexProxy,
44 pub(crate) dictionary_util: Rc<DictionaryUtil>,
45 loader_service_factory: LoaderServiceFactory,
46 pub(crate) root_node: Rc<Node>,
47 pub bind_manager: BindManagerHandle,
48 pub(crate) composite_node_spec_manager: CompositeNodeSpecManager,
49 runner: Runner,
50 driver_host_runner: Rc<DriverHostRunner>,
51 pub(crate) removal_tracker: Rc<RefCell<NodeRemovalTracker>>,
52 pub bootup_tracker: Rc<BootupTracker>,
53 driver_hosts: RefCell<Vec<Weak<dyn DriverHost>>>,
54 pub devfs: Arc<Devfs>,
55 pub(crate) memory_attributor: Rc<MemoryAttributor>,
56 launcher: Option<fidl_fuchsia_driver_loader::DriverHostLauncherProxy>,
57 pub(crate) enable_test_shutdown_delays: bool,
58 pub(crate) shutdown_test_rng: Rc<RefCell<StdRng>>,
59 pub(crate) scope: fasync::Scope,
60}
61
62impl DriverRunner {
63 #[allow(clippy::too_many_arguments)]
64 pub fn new(
65 realm: fcomponent::RealmProxy,
66 introspector: fcomponent::IntrospectorProxy,
67 dictionary_util: DictionaryUtil,
68 driver_index: fdi::DriverIndexProxy,
69 loader_service_factory: LoaderServiceFactory,
70 enable_test_shutdown_delays: bool,
71 offer_injector: OfferInjector,
72 devfs: Arc<Devfs>,
73 ) -> Rc<Self> {
74 Rc::new_cyclic(|weak_driver_runner| {
75 let bind_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
76 let bind_manager = BindManagerHandle::new(bind_manager_bridge);
77
78 let composite_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
79 let composite_node_spec_manager =
80 CompositeNodeSpecManager::new(composite_manager_bridge);
81
82 let bootup_tracker = BootupTracker::new(bind_manager.clone());
83
84 let root_node = Node::new(
85 "dev",
86 Weak::new(),
87 Box::new(DriverRunnerBridge(weak_driver_runner.clone())),
88 );
89 root_node.setup_devfs_for_root_node(devfs.root_node());
90
91 let runner = Runner::new(realm.clone(), introspector, offer_injector);
92 let driver_host_runner = DriverHostRunner::new(realm);
93 let removal_tracker = NodeRemovalTracker::new();
94 let memory_attributor = Rc::new(MemoryAttributor::new());
95
96 Self {
97 driver_index,
98 dictionary_util: Rc::new(dictionary_util),
99 loader_service_factory,
100 root_node,
101 bind_manager,
102 composite_node_spec_manager,
103 runner,
104 driver_host_runner,
105 removal_tracker,
106 bootup_tracker,
107 driver_hosts: RefCell::new(Vec::new()),
108 devfs,
109 memory_attributor,
110 launcher: None,
111 enable_test_shutdown_delays,
112 shutdown_test_rng: Rc::new(RefCell::new(StdRng::from_os_rng())),
113 scope: fasync::Scope::new_with_name("driver_runner"),
114 }
115 })
116 }
117
118 pub fn register_notifier(self: &Rc<Self>) -> Result<(), anyhow::Error> {
119 let (client, server) = create_endpoints();
120 self.driver_index.set_notifier(client)?;
121 let weak_self = Rc::downgrade(self);
122 self.scope.spawn_local(async move {
123 let mut stream = server.into_stream();
124 while let Some(Ok(msg)) = stream.next().await {
125 match msg {
126 fdi::DriverNotifierRequest::NewDriverAvailable { .. } => {
127 let Some(this) = weak_self.upgrade() else {
128 return;
129 };
130 let _ = this.bind_manager.try_bind_all_available().await;
131 }
132 }
133 }
134 });
135 Ok(())
136 }
137
138 pub fn root_node(&self) -> Rc<Node> {
139 self.root_node.clone()
140 }
141
142 pub fn get_composite_list_info(&self) -> Vec<fdd::CompositeNodeInfo> {
143 self.composite_node_spec_manager.get_composite_info()
144 }
145
146 pub fn driver_hosts(&self) -> Vec<Rc<dyn DriverHost>> {
147 self.driver_hosts.borrow().iter().filter_map(|w| w.upgrade()).collect()
148 }
149
150 pub fn get_driver_host(
151 &self,
152 driver_host_name_for_colocation: &str,
153 ) -> Option<Rc<dyn DriverHost>> {
154 if driver_host_name_for_colocation.is_empty() {
155 return None;
156 }
157 for host_weak in self.driver_hosts.borrow().iter() {
158 if let Some(host) = host_weak.upgrade()
159 && host.name_for_colocation() == driver_host_name_for_colocation
160 {
161 return Some(host);
162 }
163 }
164 None
165 }
166
167 pub async fn start_root_driver(self: &Rc<Self>, url: String) -> Result<(), zx::Status> {
168 self.bootup_tracker.start();
169 let package_type = if url.starts_with("fuchsia-boot://") {
170 fdf::DriverPackageType::Boot
171 } else {
172 fdf::DriverPackageType::Base
173 };
174
175 let (sender, receiver) = oneshot::channel();
176 self.root_node.set_driver_host_name_for_colocation("root");
177 let self_clone = self.clone();
178 self.scope.spawn_local(async move {
179 if let Err(e) = self_clone.start_driver(&self_clone.root_node, &url, package_type).await
180 {
181 error!("Failed to start root driver: {}", e);
182 sender.send(Err(e)).unwrap();
183 } else {
184 sender.send(Ok(())).unwrap();
185 }
186 });
187 receiver.await.map_err(|_| zx::Status::CANCELED)?
188 }
189
190 pub fn start_devfs_driver(self: &Rc<Self>) {
191 let self_clone = self.clone();
192 self.scope.spawn_local(async move {
193 let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
194 let result = self_clone
195 .runner
196 .create_driver_component(
197 "devfs_driver",
198 "fuchsia-boot:///devfs-driver#meta/devfs-driver.cm",
199 &Collection::Boot.to_string(),
200 &[],
201 None,
202 true,
203 server,
204 )
205 .await;
206 if let Ok((handle, receiver)) = result {
207 self_clone.devfs.set_component_controller_proxy(client);
208 if let Err(e) = self_clone.devfs.attach_component(handle, receiver).await {
209 error!("Failed to attach devfs component: {}", e);
210 }
211 } else {
212 error!("Starting the devfs component failed");
213 }
214 });
215 }
216
217 pub async fn start_driver(
218 self: &Rc<Self>,
219 node: &Rc<Node>,
220 url: &str,
221 package_type: fdf::DriverPackageType,
222 ) -> Result<(), zx::Status> {
223 node.set_collection(to_collection(node, package_type));
232 node.set_driver_package_type(package_type);
233
234 let moniker = node.make_component_moniker();
235 self.bootup_tracker.notify_new_start_request(moniker.clone(), url.to_string());
236
237 match self.start_driver_internal(node, url, &moniker).await {
238 Ok(_) => {
239 node.complete_bind(Ok(())).await;
240 self.bootup_tracker.notify_start_complete(&moniker);
241 Ok(())
242 }
243 Err(err) => {
244 node.on_start_error(err);
245 node.complete_bind(Err(err)).await;
246 self.bootup_tracker.notify_start_complete(&moniker);
247 Err(err)
248 }
249 }
250 }
251
252 async fn start_driver_internal(
253 self: &Rc<Self>,
254 node: &Rc<Node>,
255 url: &str,
256 moniker: &str,
257 ) -> Result<(), zx::Status> {
258 let dictionary = node.prepare_dictionary().await;
259
260 if !node.has_component_controller_proxy() {
261 let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
262
263 let offers = node.offers().clone();
264 let create_result = self
265 .runner
266 .create_driver_component(
267 moniker,
268 url,
269 &node.collection().to_string(),
270 &offers,
271 dictionary,
272 node.skip_injected_offers(),
273 server,
274 )
275 .await;
276
277 match create_result {
278 Ok((handle_info, receiver)) => {
279 node.set_created_info(client, handle_info, receiver).await;
280 }
281 Err(err) => {
282 return Err(err);
283 }
284 }
285 }
286
287 node.send_start_request().await?;
288 let (start_info, controller) = node.get_next_start_request().await?;
289 node.start_driver(start_info, controller).await
290 }
291
292 pub async fn create_driver_host(
293 &self,
294 use_next_vdso: bool,
295 driver_host_name_for_colocation: String,
296 ) -> Result<Rc<dyn DriverHost>, zx::Status> {
297 let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
298 let name = if !driver_host_name_for_colocation.is_empty() {
299 format!("driver-host-{}", driver_host_name_for_colocation.trim_start_matches('#'))
300 } else {
301 format!("driver-host-{}", self.driver_hosts.borrow().len())
302 };
303
304 self.create_driver_host_component(&name, exposed_dir_server, use_next_vdso)?;
305
306 let driver_host_proxy =
307 connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
308 .map_err(|_| zx::Status::INTERNAL)?;
309
310 let (tx, rx) = oneshot::channel();
311 let _ = self.loader_service_factory.unbounded_send(tx);
312 let loader_service_client = rx.await.map_err(|e| {
313 error!("Failed to connect to loader service: {}", e);
314 zx::Status::INTERNAL
315 })??;
316
317 let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
318 driver_host_proxy,
319 None,
320 ExecutionScope::new(),
321 driver_host_name_for_colocation,
322 ));
323 driver_host.install_loader(loader_service_client)?;
324
325 self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
326
327 Ok(driver_host)
328 }
329
330 pub async fn create_driver_host_dynamic_linker(
331 self: &Rc<Self>,
332 driver_host_name_for_colocation: String,
333 ) -> Result<Rc<dyn DriverHost>, zx::Status> {
334 let driver_host_runner = self.driver_host_runner.clone();
335 let launcher = self.launcher.clone().unwrap();
336 let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
337 let loader_client = driver_host_runner
338 .start_driver_host(launcher, exposed_dir_server)
339 .await
340 .map_err(|e| {
341 error!("Failed to start driver host: {e:?}");
342 zx::Status::INTERNAL
343 })?;
344
345 let driver_host_client =
346 connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
347 .map_err(|_| zx::Status::INTERNAL)?;
348 let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
349 driver_host_client,
350 Some(loader_client.into_proxy()),
351 ExecutionScope::new(),
352 driver_host_name_for_colocation,
353 ));
354 self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
355 Ok(driver_host)
356 }
357
358 fn create_driver_host_component(
359 &self,
360 moniker: &str,
361 exposed_dir: ServerEnd<fio::DirectoryMarker>,
362 use_next_vdso: bool,
363 ) -> Result<(), zx::Status> {
364 let url = if use_next_vdso {
365 "fuchsia-boot:///driver_host#meta/driver_host_next.cm"
366 } else {
367 "fuchsia-boot:///driver_host#meta/driver_host.cm"
368 };
369
370 let child_decl = fdecl::Child {
371 name: Some(moniker.to_string()),
372 url: Some(url.to_string()),
373 startup: Some(fdecl::StartupMode::Lazy),
374 ..Default::default()
375 };
376
377 let create_child_args = fcomponent::CreateChildArgs::default();
378
379 let realm = self.runner.realm.clone();
380 let child_moniker = moniker.to_string();
381 self.scope.spawn_local(async move {
382 let result = realm
383 .create_child(
384 &fdecl::CollectionRef { name: "driver-hosts".to_string() },
385 &child_decl,
386 create_child_args,
387 )
388 .await;
389
390 if let Err(e) = result {
391 error!("Failed to create driver host '{}': {}", child_moniker, e);
392 return;
393 }
394
395 let child_ref = fdecl::ChildRef {
396 name: child_moniker.clone(),
397 collection: Some("driver-hosts".to_string()),
398 };
399 let open_result = realm.open_exposed_dir(&child_ref, exposed_dir).await;
400 if let Err(e) = open_result {
401 error!(
402 "Failed to open exposed directory for driver host: '{}': {}",
403 child_moniker, e
404 );
405 }
406 });
407
408 Ok(())
409 }
410
411 pub fn publish(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'_, ()>>) {
412 self.runner.publish(fs);
413 self.driver_host_runner.publish(fs);
414 self.memory_attributor.publish(fs);
415
416 let this = self.clone();
417 fs.dir("svc").add_fidl_service(move |stream: fdf::CompositeNodeManagerRequestStream| {
418 this.serve_composite_node_manager(stream);
419 });
420
421 let this = self.clone();
422 fs.dir("svc").add_fidl_service(move |stream: fdt::NodeBusTopologyRequestStream| {
423 this.serve_node_bus_topology(stream);
424 });
425
426 let this = self.clone();
427 fs.dir("svc").add_fidl_service(move |stream: fcrash::CrashIntrospectRequestStream| {
428 this.serve_crash_introspect(stream);
429 });
430 }
431
432 pub fn serve_composite_node_manager(
433 self: &Rc<Self>,
434 mut stream: fdf::CompositeNodeManagerRequestStream,
435 ) {
436 let this = self.clone();
437 self.scope.spawn_local(async move {
438 while let Some(Ok(request)) = stream.next().await {
439 this.handle_composite_node_manager_request(request).await;
440 }
441 });
442 }
443
444 async fn handle_composite_node_manager_request(
445 self: &Rc<Self>,
446 request: fdf::CompositeNodeManagerRequest,
447 ) {
448 match request {
449 fdf::CompositeNodeManagerRequest::AddSpec { payload, responder } => {
450 let result = self.add_spec(payload).await;
451 let _ = responder.send(result);
452 }
453 fdf::CompositeNodeManagerRequest::_UnknownMethod { .. } => (),
454 }
455 }
456
457 async fn add_spec(
458 self: &Rc<Self>,
459 spec: fdf::CompositeNodeSpec,
460 ) -> Result<(), fdf::CompositeNodeSpecError> {
461 let name = spec.name.clone().ok_or(fdf::CompositeNodeSpecError::MissingArgs)?;
462
463 let parents_present = spec.parents.is_some();
464 let parents2_present = spec.parents2.is_some();
465
466 if !parents_present && !parents2_present {
467 return Err(fdf::CompositeNodeSpecError::MissingArgs);
468 }
469
470 if parents_present && parents2_present {
471 return Err(fdf::CompositeNodeSpecError::DuplicateParents);
472 }
473
474 let parents2 = if let Some(ref parents) = spec.parents {
475 if parents.is_empty() {
476 return Err(fdf::CompositeNodeSpecError::EmptyNodes);
477 }
478 parents
479 .iter()
480 .map(|parent| {
481 let bind_rules = parent.bind_rules.iter().map(to_bind_rule2).collect();
482 let properties = parent.properties.iter().map(to_property2).collect();
483 fdf::ParentSpec2 { bind_rules, properties }
484 })
485 .collect()
486 } else if let Some(ref parents2) = spec.parents2 {
487 if parents2.is_empty() {
488 return Err(fdf::CompositeNodeSpecError::EmptyNodes);
489 }
490 parents2.clone()
491 } else {
492 unreachable!();
493 };
494
495 let driver_host_name_for_colocation = spec.driver_host.clone().unwrap_or_default();
496
497 let spec_for_manager = CompositeNodeSpec::new(
498 name,
499 parents2,
500 Box::new(DriverRunnerBridge(Rc::downgrade(self))),
501 driver_host_name_for_colocation,
502 );
503
504 self.composite_node_spec_manager.add_spec(spec, spec_for_manager).await
505 }
506
507 pub fn serve_node_bus_topology(self: &Rc<Self>, mut stream: fdt::NodeBusTopologyRequestStream) {
508 let this = self.clone();
509 self.scope.spawn_local(async move {
510 while let Some(Ok(request)) = stream.next().await {
511 match request {
512 fdt::NodeBusTopologyRequest::Get { token, responder } => {
513 let result = this.get_bus_topology(token).await;
514 let _ = match result {
515 Ok(topology) => responder.send(Ok(&topology)),
516 Err(status) => responder.send(Err(status.into_raw())),
517 };
518 }
519 fdt::NodeBusTopologyRequest::_UnknownMethod { .. } => (),
520 }
521 }
522 });
523 }
524
525 async fn get_bus_topology(&self, token: zx::Event) -> Result<Vec<fdf::BusInfo>, zx::Status> {
526 let token_koid = token.basic_info()?.koid;
527
528 let node = self.find_node_by_token_koid(token_koid).await;
529
530 if let Some(node) = node { Ok(node.get_bus_topology()) } else { Err(zx::Status::NOT_FOUND) }
531 }
532
533 pub fn serve_crash_introspect(
534 self: &Rc<Self>,
535 mut stream: fcrash::CrashIntrospectRequestStream,
536 ) {
537 let this = self.clone();
538 self.scope.spawn_local(async move {
539 while let Some(Ok(request)) = stream.next().await {
540 match request {
541 fcrash::CrashIntrospectRequest::FindDriverCrash {
542 process_koid,
543 thread_koid,
544 responder,
545 } => {
546 let result = this
547 .find_driver_crash(
548 zx::Koid::from_raw(process_koid),
549 zx::Koid::from_raw(thread_koid),
550 )
551 .await;
552 let _ = match result {
553 Ok(info) => responder.send(Ok(&info)),
554 Err(status) => responder.send(Err(status.into_raw())),
555 };
556 }
557 }
558 }
559 });
560 }
561
562 async fn find_driver_crash(
563 &self,
564 process_koid: zx::Koid,
565 thread_koid: zx::Koid,
566 ) -> Result<fcrash::DriverCrashInfo, zx::Status> {
567 use zx::HandleBased;
568 let hosts = self.driver_hosts.borrow().clone();
569 for host in hosts {
570 if let Some(host) = host.upgrade()
571 && let Ok(koid) = host.get_process_koid().await
572 && koid == process_koid
573 {
574 let crash_info = host.get_crash_info(thread_koid).await?;
575 let token = crash_info.node_token.ok_or(zx::Status::INTERNAL)?;
576 let token_koid = token.into_handle().basic_info()?.koid;
577
578 let node = self.find_node_by_token_koid(token_koid).await;
579
580 if let Some(node) = node {
581 return Ok(fcrash::DriverCrashInfo {
582 node_moniker: Some(node.make_component_moniker()),
583 url: crash_info.url,
584 ..Default::default()
585 });
586 } else {
587 return Err(zx::Status::NOT_FOUND);
588 }
589 }
590 }
591 Err(zx::Status::NOT_FOUND)
592 }
593
594 async fn find_node_by_token_koid(&self, token_koid: zx::Koid) -> Option<Rc<Node>> {
595 let mut result: Option<Rc<Node>> = None;
596 perform_bfs(self.root_node(), async |current| {
597 if result.is_some() {
598 return false; }
600 if let Some(current_koid) = current.token_koid()
601 && current_koid == token_koid
602 {
603 result = Some(current.clone());
604 return false;
605 }
606 true
607 })
608 .await;
609 result
610 }
611
612 pub async fn rebind_composites_with_driver(&self, driver_url: &str) -> u32 {
613 let mut names = HashSet::new();
614 perform_bfs(self.root_node(), async |node| {
615 if node.is_composite() && node.driver_url() == driver_url {
616 names.insert(node.name().to_string());
617 return false; }
619 true })
621 .await;
622
623 let count = names.len() as u32;
624 for name in names {
625 let _ = self.composite_node_spec_manager.rebind(name, None).await;
626 }
627
628 count
629 }
630
631 pub async fn restart_nodes_colocated_with_driver_url(
632 self: &Rc<Self>,
633 url: &str,
634 rematch_flags: fdd::RestartRematchFlags,
635 ) -> Result<u32, zx::Status> {
636 let mut driver_hosts = HashSet::new();
638 perform_bfs(self.root_node(), async |node| {
639 if node.driver_url() == url
640 && let Some(host) = node.driver_host()
641 {
642 driver_hosts.insert(Rc::as_ptr(&host) as *const ());
645 }
646 true })
648 .await;
649
650 if driver_hosts.is_empty() {
651 warn!(
652 "restart_nodes_colocated_with_driver_url: no driver hosts found with url {}",
653 url
654 );
655 return Ok(0);
656 }
657
658 let driver_host_count = driver_hosts.len() as u32;
659
660 let this = self.clone();
662 perform_bfs(self.root_node(), async |node| {
663 let host_ptr = node.driver_host().map(|host| Rc::as_ptr(&host) as *const ());
664 if host_ptr.is_none() || !driver_hosts.contains(&host_ptr.unwrap()) {
665 return true;
667 }
668
669 if node.evaluate_rematch_flags(rematch_flags, url) {
671 if node.is_composite() {
672 debug!(
673 "RestartNodesColocatedWithDriverUrl rebinding composite {}",
674 node.make_component_moniker()
675 );
676 let _ = this
677 .composite_node_spec_manager
678 .rebind(node.name().to_string(), None)
679 .await;
680 } else {
681 debug!(
682 "RestartNodesColocatedWithDriverUrl restarting node with rematch {}",
683 node.make_component_moniker()
684 );
685 let (tx, _rx) = oneshot::channel();
686 node.restart_node_with_rematch(Some("".to_string()), tx);
687 }
688 } else {
689 info!(
690 "RestartNodesColocatedWithDriverUrl restarting node {}",
691 node.make_component_moniker()
692 );
693 node.restart_node();
694 }
695
696 false })
698 .await;
699
700 Ok(driver_host_count)
701 }
702
703 pub async fn restart_with_dictionary(
704 self: &Rc<Self>,
705 moniker: String,
706 dictionary: fsandbox::DictionaryRef,
707 reset_eventpair: zx::EventPair,
708 ) {
709 let imported = self.dictionary_util.import_dictionary(dictionary).await;
710 let imported = match imported {
711 Ok(imported) => imported,
712 Err(e) => {
713 error!("Failed to import dictionary: {}", e);
714 return;
715 }
716 };
717
718 let mut restarted_node: Option<Rc<Node>> = None;
719 perform_bfs(self.root_node(), async |node| {
720 if restarted_node.is_some() {
721 return false; }
723
724 if node.make_component_moniker() == moniker {
725 if node.has_subtree_dictionary() {
726 error!(concat!(
727 "RestartWithDictionary requested node id already contains a ",
728 "dictionary_ref from another RestartWithDictionary operation."
729 ));
730 return false; }
732 assert!(restarted_node.is_none(), "Multiple nodes with same moniker not possible.");
733 restarted_node = Some(node.clone());
734 node.set_subtree_dictionary(imported);
735 node.restart_node();
736 return false; }
738
739 true })
741 .await;
742
743 if let Some(restarted_node) = restarted_node {
744 self.scope.spawn_local(async move {
745 let signals = zx::Signals::EVENTPAIR_PEER_CLOSED | zx::Signals::EVENTPAIR_SIGNALED;
746 let on_signals = fasync::OnSignals::new(&reset_eventpair, signals);
747 on_signals.await.expect("failed to wait on eventpair");
748
749 info!("RestartWithDictionary operation released.");
750 restarted_node.remove_subtree_dictionary();
751 restarted_node.restart_node();
752 });
753 }
754 }
755
756 pub fn inspect(&self) -> inspect::Inspector {
757 let inspector =
758 inspect::Inspector::new(inspect::InspectorConfig::default().size(2 * 256 * 1024));
759
760 let mut roots = Vec::new();
761 let mut unique_nodes = HashSet::new();
762
763 let device_tree = inspector.root().create_child("node_topology");
764 let mut root_node_inspect = device_tree.create_child(self.root_node.name());
765
766 self.inspect_node_recursive(
767 &self.root_node,
768 &mut root_node_inspect,
769 &mut roots,
770 &mut unique_nodes,
771 );
772
773 device_tree.record(root_node_inspect);
774 inspector.root().record(device_tree);
775
776 for root in roots {
777 inspector.root().record(root);
778 }
779
780 self.bind_manager.record_inspect(inspector.root());
781
782 inspector
783 }
784
785 fn inspect_node_recursive(
786 &self,
787 node: &Rc<Node>,
788 inspect_node: &mut inspect::Node,
789 roots: &mut Vec<inspect::Node>,
790 unique_nodes: &mut HashSet<*const Node>,
791 ) {
792 let node_ptr = Rc::as_ptr(node);
793 if !unique_nodes.insert(node_ptr) {
794 return;
795 }
796
797 let offers = node.offers();
798 if !offers.is_empty() {
799 let array = inspect_node.create_string_array("offers", offers.len());
800 for (i, offer) in offers.iter().enumerate() {
801 array.set(i, &offer.service_name);
802 }
803 inspect_node.record(array);
804 }
805
806 let symbols = node.symbols();
807 if !symbols.is_empty() {
808 let array = inspect_node.create_string_array("symbols", symbols.len());
809 for (i, symbol) in symbols.iter().enumerate() {
810 if let Some(name) = &symbol.name {
811 array.set(i, name);
812 }
813 }
814 inspect_node.record(array);
815 }
816
817 if let Some(properties) = node.get_node_properties(None)
818 && !properties.is_empty()
819 {
820 inspect_node.record_child("properties", |properties_node| {
821 for (i, property) in properties.iter().enumerate() {
822 properties_node.record_child(i.to_string(), |inspect_property| {
823 inspect_property.record_string("key", &property.key);
824 match &property.value {
825 fdf::NodePropertyValue::StringValue(s) => {
826 inspect_property.record_string("value", s)
827 }
828 fdf::NodePropertyValue::IntValue(i) => {
829 inspect_property.record_uint("value", *i as u64)
830 }
831 fdf::NodePropertyValue::EnumValue(e) => {
832 inspect_property.record_string("value", e)
833 }
834 fdf::NodePropertyValue::BoolValue(b) => {
835 inspect_property.record_bool("value", *b)
836 }
837 _ => inspect_property.record_string("value", "UNKNOWN VALUE TYPE"),
838 }
839 });
840 }
841 });
842 }
843
844 inspect_node
845 .record_string("type", if node.is_composite() { "Composite Device" } else { "Device" });
846 inspect_node.record_string("topological_path", node.make_topological_path(false));
847 inspect_node.record_string("driver", node.driver_url());
848
849 for child in node.children() {
850 let mut child_inspect_node = inspect_node.create_child(child.name());
851 self.inspect_node_recursive(&child, &mut child_inspect_node, roots, unique_nodes);
852 roots.push(child_inspect_node);
853 }
854 }
855}