1use crate::bedrock::aggregate_router::{AggregateRouterFn, AggregateSource};
6use crate::bedrock::request_metadata::Metadata;
7use crate::bedrock::structured_dict::{
8 ComponentEnvironment, ComponentInput, ComponentOutput, StructuredDictMap,
9};
10use crate::bedrock::use_dictionary_router::UseDictionaryRouter;
11use crate::bedrock::with_service_renames_and_filter::WithServiceRenamesAndFilter;
12use crate::capability_source::{
13 AggregateCapability, AggregateInstance, AggregateMember, AnonymizedAggregateSource,
14 CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
15 InternalCapability, VoidSource,
16};
17use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
18use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
19use crate::{DictExt, LazyGet, Sources, WithPorcelain};
20use async_trait::async_trait;
21use cm_rust::{
22 CapabilityTypeName, DictionaryValue, ExposeDecl, ExposeDeclCommon, NativeIntoFidl, OfferDecl,
23 OfferDeclCommon, SourceName, SourcePath, UseDeclCommon,
24};
25use cm_types::{Availability, BorrowedSeparatedPath, IterablePath, Name, SeparatedPath};
26use fidl::endpoints::DiscoverableProtocolMarker;
27use fuchsia_sync::Mutex;
28use futures::FutureExt;
29use itertools::Itertools;
30use log::warn;
31use moniker::{ChildName, Moniker};
32use router_error::RouterError;
33use sandbox::{
34 Capability, CapabilityBound, Connector, Data, Dict, DirConnector, Request, Routable, Router,
35 RouterResponse, WeakInstanceToken,
36};
37use std::collections::{BTreeMap, HashMap};
38use std::fmt::Debug;
39use std::sync::{Arc, LazyLock};
40use {
41 fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
42 fidl_fuchsia_component_internal as finternal, fidl_fuchsia_io as fio,
43 fidl_fuchsia_sys2 as fsys,
44};
45
46pub type EventStreamFilter = Option<BTreeMap<String, DictionaryValue>>;
48
49#[derive(Clone)]
51pub struct EventStreamSourceRouter {
52 pub router: Router<Dict>,
55 pub filter: EventStreamFilter,
58}
59pub type EventStreamUseRouterFn<C> =
60 dyn Fn(&Arc<C>, Vec<EventStreamSourceRouter>) -> Router<Connector>;
61
62static NAMESPACE: LazyLock<Name> = LazyLock::new(|| "namespace".parse().unwrap());
63static NUMBERED_HANDLES: LazyLock<Name> = LazyLock::new(|| "numbered_handles".parse().unwrap());
64static RUNNER: LazyLock<Name> = LazyLock::new(|| "runner".parse().unwrap());
65static CONFIG: LazyLock<Name> = LazyLock::new(|| "config".parse().unwrap());
66
67#[derive(Debug, Clone)]
69pub struct ProgramInput {
70 inner: Dict,
76}
77
78impl Default for ProgramInput {
79 fn default() -> Self {
80 Self::new(Dict::new(), None, Dict::new())
81 }
82}
83
84impl From<ProgramInput> for Dict {
85 fn from(program_input: ProgramInput) -> Self {
86 program_input.inner
87 }
88}
89
90impl ProgramInput {
91 pub fn new(namespace: Dict, runner: Option<Router<Connector>>, config: Dict) -> Self {
92 let inner = Dict::new();
93 inner.insert(NAMESPACE.clone(), namespace.into()).unwrap();
94 if let Some(runner) = runner {
95 inner.insert(RUNNER.clone(), runner.into()).unwrap();
96 }
97 inner.insert(NUMBERED_HANDLES.clone(), Dict::new().into()).unwrap();
98 inner.insert(CONFIG.clone(), config.into()).unwrap();
99 ProgramInput { inner }
100 }
101
102 pub fn namespace(&self) -> Dict {
104 let cap = self.inner.get(&*NAMESPACE).expect("capabilities must be cloneable").unwrap();
105 let Capability::Dictionary(dict) = cap else {
106 unreachable!("namespace entry must be a dict: {cap:?}");
107 };
108 dict
109 }
110
111 pub fn numbered_handles(&self) -> Dict {
113 let cap =
114 self.inner.get(&*NUMBERED_HANDLES).expect("capabilities must be cloneable").unwrap();
115 let Capability::Dictionary(dict) = cap else {
116 unreachable!("numbered_handles entry must be a dict: {cap:?}");
117 };
118 dict
119 }
120
121 pub fn runner(&self) -> Option<Router<Connector>> {
123 let cap = self.inner.get(&*RUNNER).expect("capabilities must be cloneable");
124 match cap {
125 None => None,
126 Some(Capability::ConnectorRouter(r)) => Some(r),
127 cap => unreachable!("runner entry must be a router: {cap:?}"),
128 }
129 }
130
131 fn set_runner(&self, capability: Capability) {
132 self.inner.insert(RUNNER.clone(), capability).unwrap()
133 }
134
135 pub fn config(&self) -> Dict {
137 let cap = self.inner.get(&*CONFIG).expect("capabilities must be cloneable").unwrap();
138 let Capability::Dictionary(dict) = cap else {
139 unreachable!("config entry must be a dict: {cap:?}");
140 };
141 dict
142 }
143}
144
145#[derive(Debug)]
148pub struct ComponentSandbox {
149 pub component_input: ComponentInput,
151
152 pub component_output: ComponentOutput,
154
155 pub program_input: ProgramInput,
157
158 pub program_output_dict: Dict,
160
161 framework_router: Mutex<Router<Dict>>,
171
172 pub capability_sourced_capabilities_dict: Dict,
175
176 pub declared_dictionaries: Dict,
178
179 pub child_inputs: StructuredDictMap<ComponentInput>,
182
183 pub collection_inputs: StructuredDictMap<ComponentInput>,
187}
188
189impl Default for ComponentSandbox {
190 fn default() -> Self {
191 static NULL_ROUTER: LazyLock<Router<Dict>> = LazyLock::new(|| Router::new(NullRouter {}));
192 struct NullRouter;
193 #[async_trait]
194 impl Routable<Dict> for NullRouter {
195 async fn route(
196 &self,
197 _request: Option<Request>,
198 _debug: bool,
199 _target: WeakInstanceToken,
200 ) -> Result<RouterResponse<Dict>, RouterError> {
201 panic!("null router invoked");
202 }
203 }
204 let framework_router = Mutex::new(NULL_ROUTER.clone());
205 Self {
206 framework_router,
207 component_input: Default::default(),
208 component_output: Default::default(),
209 program_input: Default::default(),
210 program_output_dict: Default::default(),
211 capability_sourced_capabilities_dict: Default::default(),
212 declared_dictionaries: Default::default(),
213 child_inputs: Default::default(),
214 collection_inputs: Default::default(),
215 }
216 }
217}
218
219impl Clone for ComponentSandbox {
220 fn clone(&self) -> Self {
221 let Self {
222 component_input,
223 component_output,
224 program_input,
225 program_output_dict,
226 framework_router,
227 capability_sourced_capabilities_dict,
228 declared_dictionaries,
229 child_inputs,
230 collection_inputs,
231 } = self;
232 Self {
233 component_input: component_input.clone(),
234 component_output: component_output.clone(),
235 program_input: program_input.clone(),
236 program_output_dict: program_output_dict.clone(),
237 framework_router: Mutex::new(framework_router.lock().clone()),
238 capability_sourced_capabilities_dict: capability_sourced_capabilities_dict.clone(),
239 declared_dictionaries: declared_dictionaries.clone(),
240 child_inputs: child_inputs.clone(),
241 collection_inputs: collection_inputs.clone(),
242 }
243 }
244}
245
246impl ComponentSandbox {
247 pub fn append(&self, sandbox: &ComponentSandbox) {
250 let ComponentSandbox {
253 component_input,
254 component_output,
255 program_input,
256 program_output_dict,
257 framework_router,
258 capability_sourced_capabilities_dict,
259 declared_dictionaries,
260 child_inputs,
261 collection_inputs,
262 } = sandbox;
263 for (copy_from, copy_to) in [
264 (&component_input.capabilities(), &self.component_input.capabilities()),
265 (&component_input.environment().debug(), &self.component_input.environment().debug()),
266 (
267 &component_input.environment().runners(),
268 &self.component_input.environment().runners(),
269 ),
270 (
271 &component_input.environment().resolvers(),
272 &self.component_input.environment().resolvers(),
273 ),
274 (&component_output.capabilities(), &self.component_output.capabilities()),
275 (&component_output.framework(), &self.component_output.framework()),
276 (&program_input.namespace(), &self.program_input.namespace()),
277 (&program_input.numbered_handles(), &self.program_input.numbered_handles()),
278 (&program_input.config(), &self.program_input.config()),
279 (&program_output_dict, &self.program_output_dict),
280 (&capability_sourced_capabilities_dict, &self.capability_sourced_capabilities_dict),
281 (&declared_dictionaries, &self.declared_dictionaries),
282 ] {
283 copy_to.append(copy_from).expect("sandbox capability is not cloneable");
284 }
285 if let Some(timeout) = component_input.environment().stop_timeout() {
286 self.component_input.environment().set_stop_timeout(timeout as i64);
287 }
288 *self.framework_router.lock() = framework_router.lock().clone();
289 if let Some(runner_router) = program_input.runner() {
290 self.program_input.set_runner(runner_router.into());
291 }
292 self.child_inputs.append(child_inputs).unwrap();
293 self.collection_inputs.append(collection_inputs).unwrap();
294 }
295
296 pub fn framework_router(&self) -> Router<Dict> {
297 self.framework_router.lock().clone()
298 }
299}
300
301pub fn build_component_sandbox<C: ComponentInstanceInterface + 'static>(
304 component: &Arc<C>,
305 child_component_output_dictionary_routers: HashMap<ChildName, Router<Dict>>,
306 decl: &cm_rust::ComponentDecl,
307 component_input: ComponentInput,
308 program_output_dict: Dict,
309 framework_router: Router<Dict>,
310 capability_sourced_capabilities_dict: Dict,
311 declared_dictionaries: Dict,
312 error_reporter: impl ErrorReporter,
313 aggregate_router_fn: &AggregateRouterFn<C>,
314 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
315) -> ComponentSandbox {
316 let component_output = ComponentOutput::new();
317 let program_input = ProgramInput::default();
318 let environments: StructuredDictMap<ComponentEnvironment> = Default::default();
319 let child_inputs: StructuredDictMap<ComponentInput> = Default::default();
320 let collection_inputs: StructuredDictMap<ComponentInput> = Default::default();
321
322 for environment_decl in &decl.environments {
323 environments
324 .insert(
325 environment_decl.name.clone(),
326 build_environment(
327 component,
328 &child_component_output_dictionary_routers,
329 &component_input,
330 environment_decl,
331 &program_output_dict,
332 &error_reporter,
333 ),
334 )
335 .ok();
336 }
337
338 for child in &decl.children {
339 let environment;
340 if let Some(environment_name) = child.environment.as_ref() {
341 environment = environments.get(environment_name).expect(
342 "child references nonexistent environment, \
343 this should be prevented in manifest validation",
344 );
345 } else {
346 environment = component_input.environment();
347 }
348 let input = ComponentInput::new(environment);
349 let name = Name::new(child.name.as_str()).expect("child is static so name is not long");
350 child_inputs.insert(name, input).ok();
351 }
352
353 for collection in &decl.collections {
354 let environment;
355 if let Some(environment_name) = collection.environment.as_ref() {
356 environment = environments.get(environment_name).expect(
357 "collection references nonexistent environment, \
358 this should be prevented in manifest validation",
359 )
360 } else {
361 environment = component_input.environment();
362 }
363 let input = ComponentInput::new(environment);
364 collection_inputs.insert(collection.name.clone(), input).ok();
365 }
366
367 let mut dictionary_use_bundles = vec![];
368 for use_bundle in group_use_aggregates(&decl.uses).into_iter() {
369 let first_use = *use_bundle.first().unwrap();
370 match first_use {
371 cm_rust::UseDecl::Service(_)
372 if matches!(first_use.source(), cm_rust::UseSource::Collection(_)) =>
373 {
374 let cm_rust::UseSource::Collection(collection_name) = first_use.source() else {
375 unreachable!();
376 };
377 let availability = *first_use.availability();
378 let aggregate = (aggregate_router_fn)(
379 component.clone(),
380 vec![AggregateSource::Collection { collection_name: collection_name.clone() }],
381 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
382 capability: AggregateCapability::Service(first_use.source_name().clone()),
383 moniker: component.moniker().clone(),
384 members: vec![AggregateMember::try_from(first_use).unwrap()],
385 sources: Sources::new(cm_rust::CapabilityTypeName::Service),
386 instances: vec![],
387 }),
388 )
389 .with_porcelain_with_default(CapabilityTypeName::Service)
390 .availability(availability)
391 .target(component)
392 .error_info(first_use)
393 .error_reporter(error_reporter.clone())
394 .build();
395 if let Err(e) = program_input
396 .namespace()
397 .insert_capability(first_use.path().unwrap(), aggregate.into())
398 {
399 warn!(
400 "failed to insert {} in program input dict: {e:?}",
401 first_use.path().unwrap()
402 )
403 }
404 }
405 cm_rust::UseDecl::Service(_) => extend_dict_with_use::<DirConnector, _>(
406 component,
407 &child_component_output_dictionary_routers,
408 &component_input,
409 &program_input,
410 &program_output_dict,
411 &framework_router,
412 &capability_sourced_capabilities_dict,
413 first_use,
414 error_reporter.clone(),
415 ),
416 cm_rust::UseDecl::Directory(_) => extend_dict_with_use::<DirConnector, C>(
417 component,
418 &child_component_output_dictionary_routers,
419 &component_input,
420 &program_input,
421 &program_output_dict,
422 &framework_router,
423 &capability_sourced_capabilities_dict,
424 first_use,
425 error_reporter.clone(),
426 ),
427 cm_rust::UseDecl::Protocol(_) | cm_rust::UseDecl::Runner(_) => {
428 extend_dict_with_use::<Connector, _>(
429 component,
430 &child_component_output_dictionary_routers,
431 &component_input,
432 &program_input,
433 &program_output_dict,
434 &framework_router,
435 &capability_sourced_capabilities_dict,
436 first_use,
437 error_reporter.clone(),
438 )
439 }
440 cm_rust::UseDecl::Config(config) => extend_dict_with_config_use(
441 component,
442 &child_component_output_dictionary_routers,
443 &component_input,
444 &program_input,
445 &program_output_dict,
446 config,
447 error_reporter.clone(),
448 ),
449 cm_rust::UseDecl::EventStream(_) => extend_dict_with_event_stream_uses(
450 component,
451 &component_input,
452 &program_input,
453 use_bundle,
454 error_reporter.clone(),
455 event_stream_use_router_fn,
456 ),
457 cm_rust::UseDecl::Dictionary(_) => {
458 dictionary_use_bundles.push(use_bundle);
459 }
460 _ => (),
461 }
462 }
463
464 if !decl.uses.iter().any(|u| matches!(u, cm_rust::UseDecl::Runner(_))) {
468 if let Some(runner_name) = decl.program.as_ref().and_then(|p| p.runner.as_ref()) {
469 extend_dict_with_use::<Connector, _>(
470 component,
471 &child_component_output_dictionary_routers,
472 &component_input,
473 &program_input,
474 &program_output_dict,
475 &framework_router,
476 &capability_sourced_capabilities_dict,
477 &cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
478 source: cm_rust::UseSource::Environment,
479 source_name: runner_name.clone(),
480 source_dictionary: Default::default(),
481 }),
482 error_reporter.clone(),
483 )
484 }
485 }
486
487 for dictionary_use_bundle in dictionary_use_bundles {
494 extend_dict_with_dictionary_use(
495 component,
496 &child_component_output_dictionary_routers,
497 &component_input,
498 &program_input,
499 &program_output_dict,
500 &framework_router,
501 &capability_sourced_capabilities_dict,
502 dictionary_use_bundle,
503 error_reporter.clone(),
504 )
505 }
506
507 for offer_bundle in group_offer_aggregates(&decl.offers).into_iter() {
508 let first_offer = offer_bundle.first().unwrap();
509 let get_target_dict = || match first_offer.target() {
510 cm_rust::OfferTarget::Child(child_ref) => {
511 assert!(child_ref.collection.is_none(), "unexpected dynamic offer target");
512 let child_name = Name::new(child_ref.name.as_str())
513 .expect("child is static so name is not long");
514 if child_inputs.get(&child_name).is_none() {
515 child_inputs.insert(child_name.clone(), Default::default()).ok();
516 }
517 child_inputs
518 .get(&child_name)
519 .expect("component input was just added")
520 .capabilities()
521 }
522 cm_rust::OfferTarget::Collection(name) => {
523 if collection_inputs.get(&name).is_none() {
524 collection_inputs.insert(name.clone(), Default::default()).ok();
525 }
526 collection_inputs
527 .get(&name)
528 .expect("collection input was just added")
529 .capabilities()
530 }
531 cm_rust::OfferTarget::Capability(name) => {
532 let dict = match declared_dictionaries
533 .get(name)
534 .expect("dictionaries must be cloneable")
535 {
536 Some(dict) => dict,
537 None => {
538 let dict = Dict::new();
539 declared_dictionaries
540 .insert(name.clone(), Capability::Dictionary(dict.clone()))
541 .ok();
542 Capability::Dictionary(dict)
543 }
544 };
545 let Capability::Dictionary(dict) = dict else {
546 panic!("wrong type in dict");
547 };
548 dict
549 }
550 };
551 match first_offer {
552 cm_rust::OfferDecl::Service(_)
553 if offer_bundle.len() == 1
554 && !matches!(first_offer.source(), cm_rust::OfferSource::Collection(_)) =>
555 {
556 extend_dict_with_offer::<DirConnector, _>(
557 component,
558 &child_component_output_dictionary_routers,
559 &component_input,
560 &program_output_dict,
561 &framework_router,
562 &capability_sourced_capabilities_dict,
563 first_offer,
564 &(get_target_dict)(),
565 error_reporter.clone(),
566 )
567 }
568 cm_rust::OfferDecl::Service(_) => {
569 let aggregate_router = new_aggregate_router_from_service_offers(
570 &offer_bundle,
571 component,
572 &child_component_output_dictionary_routers,
573 &component_input,
574 &program_output_dict,
575 &framework_router,
576 &capability_sourced_capabilities_dict,
577 error_reporter.clone(),
578 aggregate_router_fn,
579 );
580 (get_target_dict)()
581 .insert(first_offer.target_name().clone(), aggregate_router.into())
582 .expect("failed to insert capability into target dict");
583 }
584 cm_rust::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
585 component,
586 &child_component_output_dictionary_routers,
587 &component_input,
588 &program_output_dict,
589 &framework_router,
590 &capability_sourced_capabilities_dict,
591 first_offer,
592 &(get_target_dict)(),
593 error_reporter.clone(),
594 ),
595 cm_rust::OfferDecl::Directory(_) => extend_dict_with_offer::<DirConnector, _>(
596 component,
597 &child_component_output_dictionary_routers,
598 &component_input,
599 &program_output_dict,
600 &framework_router,
601 &capability_sourced_capabilities_dict,
602 first_offer,
603 &(get_target_dict)(),
604 error_reporter.clone(),
605 ),
606 cm_rust::OfferDecl::Dictionary(_) | cm_rust::OfferDecl::EventStream(_) => {
607 extend_dict_with_offer::<Dict, _>(
608 component,
609 &child_component_output_dictionary_routers,
610 &component_input,
611 &program_output_dict,
612 &framework_router,
613 &capability_sourced_capabilities_dict,
614 first_offer,
615 &(get_target_dict)(),
616 error_reporter.clone(),
617 )
618 }
619 cm_rust::OfferDecl::Protocol(_)
620 | cm_rust::OfferDecl::Runner(_)
621 | cm_rust::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
622 component,
623 &child_component_output_dictionary_routers,
624 &component_input,
625 &program_output_dict,
626 &framework_router,
627 &capability_sourced_capabilities_dict,
628 first_offer,
629 &(get_target_dict)(),
630 error_reporter.clone(),
631 ),
632 _ => {}
633 }
634 }
635
636 for expose_bundle in group_expose_aggregates(&decl.exposes) {
637 let first_expose = expose_bundle.first().unwrap();
638 match first_expose {
639 cm_rust::ExposeDecl::Service(_)
640 if expose_bundle.len() == 1
641 && !matches!(first_expose.source(), cm_rust::ExposeSource::Collection(_)) =>
642 {
643 extend_dict_with_expose::<DirConnector, _>(
644 component,
645 &child_component_output_dictionary_routers,
646 &program_output_dict,
647 &framework_router,
648 &capability_sourced_capabilities_dict,
649 first_expose,
650 &component_output,
651 error_reporter.clone(),
652 )
653 }
654 cm_rust::ExposeDecl::Service(_) => {
655 let mut aggregate_sources = vec![];
656 let temp_component_output = ComponentOutput::new();
657 for expose in expose_bundle.iter() {
658 extend_dict_with_expose::<DirConnector, _>(
659 component,
660 &child_component_output_dictionary_routers,
661 &program_output_dict,
662 &framework_router,
663 &capability_sourced_capabilities_dict,
664 expose,
665 &temp_component_output,
666 error_reporter.clone(),
667 );
668 match temp_component_output.capabilities().remove(first_expose.target_name()) {
669 Some(Capability::DirConnectorRouter(router)) => {
670 let source_instance = match expose.source() {
671 cm_rust::ExposeSource::Self_ => AggregateInstance::Self_,
672 cm_rust::ExposeSource::Child(name) => AggregateInstance::Child(
673 moniker::ChildName::new(name.clone().to_long(), None),
674 ),
675 other_source => {
676 warn!(
677 "unsupported source found in expose aggregate: {:?}",
678 other_source
679 );
680 continue;
681 }
682 };
683 aggregate_sources
684 .push(AggregateSource::DirectoryRouter { source_instance, router })
685 }
686 None => match expose.source() {
687 cm_rust::ExposeSource::Collection(collection_name) => {
688 aggregate_sources.push(AggregateSource::Collection {
689 collection_name: collection_name.clone(),
690 });
691 }
692 _ => continue,
693 },
694 other_value => panic!("unexpected dictionary entry: {:?}", other_value),
695 }
696 }
697 let availability = *first_expose.availability();
698 let aggregate = (aggregate_router_fn)(
699 component.clone(),
700 aggregate_sources,
701 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
702 capability: AggregateCapability::Service(
703 first_expose.target_name().clone(),
704 ),
705 moniker: component.moniker().clone(),
706 members: expose_bundle
707 .iter()
708 .filter_map(|e| AggregateMember::try_from(*e).ok())
709 .collect(),
710 sources: Sources::new(cm_rust::CapabilityTypeName::Service),
711 instances: vec![],
712 }),
713 );
714 let router = aggregate
715 .with_porcelain_with_default(CapabilityTypeName::Service)
716 .availability(availability)
717 .target(component)
718 .error_info(*first_expose)
719 .error_reporter(error_reporter.clone())
720 .build();
721 component_output
722 .capabilities()
723 .insert(first_expose.target_name().clone(), router.into())
724 .expect("failed to insert capability into target dict")
725 }
726 cm_rust::ExposeDecl::Config(_) => extend_dict_with_expose::<Data, _>(
727 component,
728 &child_component_output_dictionary_routers,
729 &program_output_dict,
730 &framework_router,
731 &capability_sourced_capabilities_dict,
732 first_expose,
733 &component_output,
734 error_reporter.clone(),
735 ),
736 cm_rust::ExposeDecl::Dictionary(_) => extend_dict_with_expose::<Dict, _>(
737 component,
738 &child_component_output_dictionary_routers,
739 &program_output_dict,
740 &framework_router,
741 &capability_sourced_capabilities_dict,
742 first_expose,
743 &component_output,
744 error_reporter.clone(),
745 ),
746 cm_rust::ExposeDecl::Directory(_) => extend_dict_with_expose::<DirConnector, _>(
747 component,
748 &child_component_output_dictionary_routers,
749 &program_output_dict,
750 &framework_router,
751 &capability_sourced_capabilities_dict,
752 first_expose,
753 &component_output,
754 error_reporter.clone(),
755 ),
756 cm_rust::ExposeDecl::Protocol(_)
757 | cm_rust::ExposeDecl::Runner(_)
758 | cm_rust::ExposeDecl::Resolver(_) => extend_dict_with_expose::<Connector, _>(
759 component,
760 &child_component_output_dictionary_routers,
761 &program_output_dict,
762 &framework_router,
763 &capability_sourced_capabilities_dict,
764 first_expose,
765 &component_output,
766 error_reporter.clone(),
767 ),
768 }
769 }
770
771 ComponentSandbox {
772 component_input,
773 component_output,
774 program_input,
775 program_output_dict,
776 framework_router: Mutex::new(framework_router),
777 capability_sourced_capabilities_dict,
778 declared_dictionaries,
779 child_inputs,
780 collection_inputs,
781 }
782}
783
784fn new_aggregate_router_from_service_offers<C: ComponentInstanceInterface + 'static>(
785 offer_bundle: &Vec<&cm_rust::OfferDecl>,
786 component: &Arc<C>,
787 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
788 component_input: &ComponentInput,
789 program_output_dict: &Dict,
790 framework_router: &Router<Dict>,
791 capability_sourced_capabilities_dict: &Dict,
792 error_reporter: impl ErrorReporter,
793 aggregate_router_fn: &AggregateRouterFn<C>,
794) -> Router<DirConnector> {
795 let mut aggregate_sources = vec![];
796 let dict_for_source_router = Dict::new();
797 let source = new_aggregate_capability_source(component.moniker().clone(), offer_bundle.clone());
798 for offer in offer_bundle.iter() {
799 if matches!(&source, &CapabilitySource::FilteredAggregateProvider(_)) {
800 if let cm_rust::OfferDecl::Service(offer_service_decl) = offer {
801 if offer_service_decl
802 .source_instance_filter
803 .as_ref()
804 .and_then(|v| v.first())
805 .is_none()
806 && offer_service_decl
807 .renamed_instances
808 .as_ref()
809 .and_then(|v| v.first())
810 .is_none()
811 {
812 continue;
816 }
817 }
818 }
819 extend_dict_with_offer::<DirConnector, _>(
820 component,
821 &child_component_output_dictionary_routers,
822 &component_input,
823 &program_output_dict,
824 framework_router,
825 &capability_sourced_capabilities_dict,
826 offer,
827 &dict_for_source_router,
828 error_reporter.clone(),
829 );
830 match dict_for_source_router.remove(offer.target_name()) {
831 Some(Capability::DirConnectorRouter(router)) => {
832 let source_instance = match offer.source() {
833 cm_rust::OfferSource::Self_ => AggregateInstance::Self_,
834 cm_rust::OfferSource::Parent => AggregateInstance::Parent,
835 cm_rust::OfferSource::Child(child_ref) => {
836 AggregateInstance::Child(moniker::ChildName::new(
837 child_ref.name.clone(),
838 child_ref.collection.clone(),
839 ))
840 }
841 other_source => {
842 warn!("unsupported source found in offer aggregate: {:?}", other_source);
843 continue;
844 }
845 };
846 aggregate_sources.push(AggregateSource::DirectoryRouter { source_instance, router })
847 }
848 None => match offer.source() {
849 cm_rust::OfferSource::Collection(collection_name) => {
853 aggregate_sources.push(AggregateSource::Collection {
854 collection_name: collection_name.clone(),
855 });
856 }
857 _ => continue,
858 },
859 other => warn!("found unexpected entry in dictionary: {:?}", other),
860 }
861 }
862 (aggregate_router_fn)(component.clone(), aggregate_sources, source)
863}
864
865fn new_aggregate_capability_source(
866 moniker: Moniker,
867 offers: Vec<&cm_rust::OfferDecl>,
868) -> CapabilitySource {
869 let offer_service_decls = offers
870 .iter()
871 .map(|o| match o {
872 cm_rust::OfferDecl::Service(o) => o,
873 _ => panic!(
874 "cannot aggregate non-service capabilities, manifest validation should prevent this"
875 ),
876 })
877 .collect::<Vec<_>>();
878 let is_filtered_offer = offer_service_decls.iter().any(|o| {
880 o.source_instance_filter.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
881 || o.renamed_instances.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
882 });
883 let capability =
884 AggregateCapability::Service(offer_service_decls.first().unwrap().target_name.clone());
885 if is_filtered_offer {
886 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
887 capability,
888 moniker,
889 offer_service_decls: offer_service_decls.into_iter().cloned().collect(),
890 sources: Sources::new(cm_rust::CapabilityTypeName::Service).component().collection(),
891 })
892 } else {
893 let members = offers.iter().filter_map(|o| AggregateMember::try_from(*o).ok()).collect();
894 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
895 capability,
896 moniker,
897 members,
898 sources: Sources::new(cm_rust::CapabilityTypeName::Service).component().collection(),
899 instances: vec![],
900 })
901 }
902}
903
904fn group_use_aggregates(uses: &[cm_rust::UseDecl]) -> Vec<Vec<&cm_rust::UseDecl>> {
908 let mut groupings = HashMap::new();
909 let mut ungroupable_uses = vec![];
910 for use_ in uses.iter() {
911 if let Some(target_path) = use_.path() {
912 groupings.entry(target_path).or_insert(vec![]).push(use_);
913 } else {
914 ungroupable_uses.push(vec![use_]);
915 }
916 }
917 groupings
918 .into_iter()
919 .map(|(_key, grouping)| grouping)
920 .chain(ungroupable_uses.into_iter())
921 .collect()
922}
923
924fn group_offer_aggregates(offers: &[cm_rust::OfferDecl]) -> Vec<Vec<&cm_rust::OfferDecl>> {
928 let mut groupings = HashMap::new();
929 for offer in offers {
930 groupings.entry((offer.target(), offer.target_name())).or_insert(vec![]).push(offer);
931 }
932 groupings.into_iter().map(|(_key, grouping)| grouping).collect()
933}
934
935fn group_expose_aggregates(exposes: &[cm_rust::ExposeDecl]) -> Vec<Vec<&cm_rust::ExposeDecl>> {
937 let mut groupings = HashMap::new();
938 for expose in exposes {
939 groupings.entry((expose.target(), expose.target_name())).or_insert(vec![]).push(expose);
940 }
941 groupings.into_iter().map(|(_key, grouping)| grouping).collect()
942}
943
944fn build_environment<C: ComponentInstanceInterface + 'static>(
945 component: &Arc<C>,
946 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
947 component_input: &ComponentInput,
948 environment_decl: &cm_rust::EnvironmentDecl,
949 program_output_dict: &Dict,
950 error_reporter: &impl ErrorReporter,
951) -> ComponentEnvironment {
952 let mut environment = ComponentEnvironment::new();
953 if environment_decl.extends == fdecl::EnvironmentExtends::Realm {
954 if let Ok(e) = component_input.environment().shallow_copy() {
955 environment = e;
956 } else {
957 warn!("failed to copy component_input.environment");
958 }
959 }
960 environment.set_name(&environment_decl.name);
961 if let Some(stop_timeout_ms) = environment_decl.stop_timeout_ms {
962 environment.set_stop_timeout(stop_timeout_ms as i64);
963 }
964 let debug = environment_decl.debug_capabilities.iter().map(|debug_registration| {
965 let cm_rust::DebugRegistration::Protocol(debug) = debug_registration;
966 (
967 &debug.source_name,
968 debug.target_name.clone(),
969 &debug.source,
970 CapabilityTypeName::Protocol,
971 RouteRequestErrorInfo::from(debug_registration),
972 )
973 });
974 let runners = environment_decl.runners.iter().map(|runner| {
975 (
976 &runner.source_name,
977 runner.target_name.clone(),
978 &runner.source,
979 CapabilityTypeName::Runner,
980 RouteRequestErrorInfo::from(runner),
981 )
982 });
983 let resolvers = environment_decl.resolvers.iter().map(|resolver| {
984 (
985 &resolver.resolver,
986 Name::new(&resolver.scheme).unwrap(),
987 &resolver.source,
988 CapabilityTypeName::Resolver,
989 RouteRequestErrorInfo::from(resolver),
990 )
991 });
992 let moniker = component.moniker();
993 for (source_name, target_name, source, porcelain_type, route_request) in
994 debug.chain(runners).chain(resolvers)
995 {
996 let source_path =
997 SeparatedPath { dirname: Default::default(), basename: source_name.clone() };
998 let router: Router<Connector> = match &source {
999 cm_rust::RegistrationSource::Parent => {
1000 use_from_parent_router::<Connector>(component_input, source_path, moniker)
1001 }
1002 cm_rust::RegistrationSource::Self_ => program_output_dict
1003 .get_router_or_not_found::<Connector>(
1004 &source_path,
1005 RoutingError::use_from_self_not_found(
1006 moniker,
1007 source_path.iter_segments().join("/"),
1008 ),
1009 ),
1010 cm_rust::RegistrationSource::Child(child_name) => {
1011 let child_name = ChildName::parse(child_name).expect("invalid child name");
1012 let Some(child_component_output) =
1013 child_component_output_dictionary_routers.get(&child_name)
1014 else {
1015 continue;
1016 };
1017 child_component_output.clone().lazy_get(
1018 source_path,
1019 RoutingError::use_from_child_expose_not_found(
1020 &child_name,
1021 moniker,
1022 source_name.clone(),
1023 ),
1024 )
1025 }
1026 };
1027 let router = router
1028 .with_porcelain_no_default(porcelain_type)
1029 .availability(Availability::Required)
1030 .target(component)
1031 .error_info(route_request)
1032 .error_reporter(error_reporter.clone());
1033 let dict_to_insert_to = match porcelain_type {
1034 CapabilityTypeName::Protocol => environment.debug(),
1035 CapabilityTypeName::Runner => environment.runners(),
1036 CapabilityTypeName::Resolver => environment.resolvers(),
1037 c => panic!("unexpected capability type {}", c),
1038 };
1039 match dict_to_insert_to.insert_capability(&target_name, router.into()) {
1040 Ok(()) => (),
1041 Err(_e) => {
1042 }
1046 }
1047 }
1048 environment
1049}
1050
1051pub fn extend_dict_with_offers<C: ComponentInstanceInterface + 'static>(
1053 component: &Arc<C>,
1054 static_offers: &[cm_rust::OfferDecl],
1055 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1056 component_input: &ComponentInput,
1057 dynamic_offers: &[cm_rust::OfferDecl],
1058 program_output_dict: &Dict,
1059 framework_router: &Router<Dict>,
1060 capability_sourced_capabilities_dict: &Dict,
1061 target_input: &ComponentInput,
1062 error_reporter: impl ErrorReporter,
1063 aggregate_router_fn: &AggregateRouterFn<C>,
1064) {
1065 for offer_bundle in group_offer_aggregates(dynamic_offers).into_iter() {
1066 let first_offer = offer_bundle.first().unwrap();
1067 match first_offer {
1068 cm_rust::OfferDecl::Service(_) => {
1069 let static_offer_bundles = group_offer_aggregates(static_offers);
1070 let maybe_static_offer_bundle = static_offer_bundles.into_iter().find(|bundle| {
1071 bundle.first().unwrap().target_name() == first_offer.target_name()
1072 });
1073 let mut combined_offer_bundle = offer_bundle.clone();
1074 if let Some(mut static_offer_bundle) = maybe_static_offer_bundle {
1075 let _ = target_input.capabilities().remove(first_offer.target_name());
1080 combined_offer_bundle.append(&mut static_offer_bundle);
1081 }
1082 if combined_offer_bundle.len() == 1
1083 && !matches!(first_offer.source(), cm_rust::OfferSource::Collection(_))
1084 {
1085 extend_dict_with_offer::<DirConnector, _>(
1086 component,
1087 &child_component_output_dictionary_routers,
1088 &component_input,
1089 &program_output_dict,
1090 framework_router,
1091 &capability_sourced_capabilities_dict,
1092 first_offer,
1093 &target_input.capabilities(),
1094 error_reporter.clone(),
1095 )
1096 } else {
1097 let aggregate_router = new_aggregate_router_from_service_offers(
1098 &combined_offer_bundle,
1099 component,
1100 &child_component_output_dictionary_routers,
1101 &component_input,
1102 &program_output_dict,
1103 framework_router,
1104 &capability_sourced_capabilities_dict,
1105 error_reporter.clone(),
1106 aggregate_router_fn,
1107 );
1108 target_input
1109 .capabilities()
1110 .insert(first_offer.target_name().clone(), aggregate_router.into())
1111 .expect("failed to insert capability into target dict");
1112 }
1113 }
1114 cm_rust::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
1115 component,
1116 &child_component_output_dictionary_routers,
1117 component_input,
1118 program_output_dict,
1119 framework_router,
1120 capability_sourced_capabilities_dict,
1121 first_offer,
1122 &target_input.capabilities(),
1123 error_reporter.clone(),
1124 ),
1125 cm_rust::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dict, _>(
1126 component,
1127 &child_component_output_dictionary_routers,
1128 component_input,
1129 program_output_dict,
1130 framework_router,
1131 capability_sourced_capabilities_dict,
1132 first_offer,
1133 &target_input.capabilities(),
1134 error_reporter.clone(),
1135 ),
1136 cm_rust::OfferDecl::Directory(_) => extend_dict_with_offer::<DirConnector, _>(
1137 component,
1138 &child_component_output_dictionary_routers,
1139 component_input,
1140 program_output_dict,
1141 framework_router,
1142 capability_sourced_capabilities_dict,
1143 first_offer,
1144 &target_input.capabilities(),
1145 error_reporter.clone(),
1146 ),
1147 cm_rust::OfferDecl::Protocol(_)
1148 | cm_rust::OfferDecl::Runner(_)
1149 | cm_rust::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
1150 component,
1151 &child_component_output_dictionary_routers,
1152 component_input,
1153 program_output_dict,
1154 framework_router,
1155 capability_sourced_capabilities_dict,
1156 first_offer,
1157 &target_input.capabilities(),
1158 error_reporter.clone(),
1159 ),
1160 _ => {}
1161 }
1162 }
1163}
1164
1165pub fn is_supported_use(use_: &cm_rust::UseDecl) -> bool {
1166 matches!(
1167 use_,
1168 cm_rust::UseDecl::Config(_)
1169 | cm_rust::UseDecl::Protocol(_)
1170 | cm_rust::UseDecl::Runner(_)
1171 | cm_rust::UseDecl::Service(_)
1172 | cm_rust::UseDecl::Directory(_)
1173 | cm_rust::UseDecl::EventStream(_)
1174 | cm_rust::UseDecl::Dictionary(_)
1175 )
1176}
1177
1178fn extend_dict_with_config_use<C: ComponentInstanceInterface + 'static>(
1181 component: &Arc<C>,
1182 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1183 component_input: &ComponentInput,
1184 program_input: &ProgramInput,
1185 program_output_dict: &Dict,
1186 config_use: &cm_rust::UseConfigurationDecl,
1187 error_reporter: impl ErrorReporter,
1188) {
1189 let moniker = component.moniker();
1190 let source_path = config_use.source_path();
1191 let porcelain_type = CapabilityTypeName::Config;
1192 let router: Router<Data> = match config_use.source() {
1193 cm_rust::UseSource::Parent => {
1194 use_from_parent_router::<Data>(component_input, source_path.to_owned(), moniker)
1195 }
1196 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<Data>(
1197 &source_path,
1198 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1199 ),
1200 cm_rust::UseSource::Child(child_name) => {
1201 let child_name = ChildName::parse(child_name).expect("invalid child name");
1202 let Some(child_component_output) =
1203 child_component_output_dictionary_routers.get(&child_name)
1204 else {
1205 panic!(
1206 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1207 moniker, child_name
1208 );
1209 };
1210 child_component_output.clone().lazy_get(
1211 source_path.to_owned(),
1212 RoutingError::use_from_child_expose_not_found(
1213 &child_name,
1214 &moniker,
1215 config_use.source_name().clone(),
1216 ),
1217 )
1218 }
1219 cm_rust::UseSource::Environment => return,
1221 cm_rust::UseSource::Debug => return,
1222 cm_rust::UseSource::Capability(_) => return,
1223 cm_rust::UseSource::Framework => return,
1224 cm_rust::UseSource::Collection(_) => return,
1225 };
1226
1227 let availability = *config_use.availability();
1228 match program_input.config().insert_capability(
1229 &config_use.target_name,
1230 router
1231 .with_porcelain_with_default(porcelain_type)
1232 .availability(availability)
1233 .target(component)
1234 .error_info(config_use)
1235 .error_reporter(error_reporter)
1236 .into(),
1237 ) {
1238 Ok(()) => (),
1239 Err(e) => {
1240 warn!("failed to insert {} in program input dict: {e:?}", config_use.target_name)
1241 }
1242 }
1243}
1244
1245fn extend_dict_with_event_stream_uses<C: ComponentInstanceInterface + 'static>(
1246 component: &Arc<C>,
1247 component_input: &ComponentInput,
1248 program_input: &ProgramInput,
1249 uses: Vec<&cm_rust::UseDecl>,
1250 error_reporter: impl ErrorReporter,
1251 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
1252) {
1253 let use_event_stream_decls = uses.into_iter().map(|u| match u {
1254 cm_rust::UseDecl::EventStream(decl) => decl,
1255 _other_use => panic!("conflicting use types share target path, this should be prevented by manifest validation"),
1256 }).collect::<Vec<_>>();
1257 let moniker = component.moniker();
1258 let porcelain_type = CapabilityTypeName::EventStream;
1259 let target_path = use_event_stream_decls.first().unwrap().target_path.clone();
1260 for use_event_stream_decl in &use_event_stream_decls {
1261 assert_eq!(
1262 &use_event_stream_decl.source,
1263 &cm_rust::UseSource::Parent,
1264 "event streams can only be used from parent, anything else should be caught by \
1265 manifest validation",
1266 );
1267 }
1268 let routers = use_event_stream_decls
1269 .into_iter()
1270 .map(|use_event_stream_decl| {
1271 let mut route_metadata = finternal::EventStreamRouteMetadata::default();
1272 if let Some(scope) = &use_event_stream_decl.scope {
1273 route_metadata.scope_moniker = Some(component.moniker().to_string());
1274 route_metadata.scope = Some(scope.clone().native_into_fidl());
1275 }
1276
1277 let source_path = use_event_stream_decl.source_path().to_owned();
1278 let router = use_from_parent_router::<Dict>(component_input, source_path, &moniker)
1279 .with_porcelain_with_default(porcelain_type)
1280 .availability(use_event_stream_decl.availability)
1281 .event_stream_route_metadata(route_metadata)
1282 .target(component)
1283 .error_info(RouteRequestErrorInfo::from(use_event_stream_decl))
1284 .error_reporter(error_reporter.clone())
1285 .build();
1286 let filter = use_event_stream_decl.filter.clone();
1287 EventStreamSourceRouter { router, filter }
1288 })
1289 .collect::<Vec<_>>();
1290
1291 let router = event_stream_use_router_fn(component, routers);
1292 if let Err(e) = program_input.namespace().insert_capability(&target_path, router.into()) {
1293 warn!("failed to insert {} in program input dict: {e:?}", target_path)
1294 }
1295}
1296
1297fn extend_dict_with_use<T, C: ComponentInstanceInterface + 'static>(
1298 component: &Arc<C>,
1299 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1300 component_input: &ComponentInput,
1301 program_input: &ProgramInput,
1302 program_output_dict: &Dict,
1303 framework_router: &Router<Dict>,
1304 capability_sourced_capabilities_dict: &Dict,
1305 use_: &cm_rust::UseDecl,
1306 error_reporter: impl ErrorReporter,
1307) where
1308 T: CapabilityBound + Clone,
1309 Router<T>: TryFrom<Capability> + Into<Capability>,
1310{
1311 if !is_supported_use(use_) {
1312 return;
1313 }
1314 let moniker = component.moniker();
1315 if let cm_rust::UseDecl::Config(config) = use_ {
1316 return extend_dict_with_config_use(
1317 component,
1318 child_component_output_dictionary_routers,
1319 component_input,
1320 program_input,
1321 program_output_dict,
1322 config,
1323 error_reporter,
1324 );
1325 };
1326
1327 let source_path = use_.source_path();
1328 let porcelain_type = CapabilityTypeName::from(use_);
1329 let router: Router<T> = match use_.source() {
1330 cm_rust::UseSource::Parent => {
1331 use_from_parent_router::<T>(component_input, source_path.to_owned(), moniker)
1332 }
1333 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1334 &source_path,
1335 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1336 ),
1337 cm_rust::UseSource::Child(child_name) => {
1338 let child_name = ChildName::parse(child_name).expect("invalid child name");
1339 let Some(child_component_output) =
1340 child_component_output_dictionary_routers.get(&child_name)
1341 else {
1342 panic!(
1343 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1344 moniker, child_name
1345 );
1346 };
1347 child_component_output.clone().lazy_get(
1348 source_path.to_owned(),
1349 RoutingError::use_from_child_expose_not_found(
1350 &child_name,
1351 &moniker,
1352 use_.source_name().clone(),
1353 ),
1354 )
1355 }
1356 cm_rust::UseSource::Framework if use_.is_from_dictionary() => {
1357 Router::<T>::new_error(RoutingError::capability_from_framework_not_found(
1358 moniker,
1359 source_path.iter_segments().join("/"),
1360 ))
1361 }
1362 cm_rust::UseSource::Framework => {
1363 query_framework_router_or_not_found(framework_router, &source_path, component)
1364 }
1365 cm_rust::UseSource::Capability(capability_name) => {
1366 let err = RoutingError::capability_from_capability_not_found(
1367 moniker,
1368 capability_name.as_str().to_string(),
1369 );
1370 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1371 || source_path.iter_segments().join("/")
1372 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1373 {
1374 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1375 } else {
1376 Router::<T>::new_error(err)
1377 }
1378 }
1379 cm_rust::UseSource::Debug => {
1380 let cm_rust::UseDecl::Protocol(use_protocol) = use_ else {
1381 panic!(
1382 "non-protocol capability used with a debug source, this should be prevented by manifest validation"
1383 );
1384 };
1385 component_input.environment().debug().get_router_or_not_found::<T>(
1386 &use_protocol.source_name,
1387 RoutingError::use_from_environment_not_found(
1388 moniker,
1389 "protocol",
1390 &use_protocol.source_name,
1391 ),
1392 )
1393 }
1394 cm_rust::UseSource::Environment => {
1395 let cm_rust::UseDecl::Runner(use_runner) = use_ else {
1396 panic!(
1397 "non-runner capability used with an environment source, this should be prevented by manifest validation"
1398 );
1399 };
1400 component_input.environment().runners().get_router_or_not_found::<T>(
1401 &use_runner.source_name,
1402 RoutingError::use_from_environment_not_found(
1403 moniker,
1404 "runner",
1405 &use_runner.source_name,
1406 ),
1407 )
1408 }
1409 cm_rust::UseSource::Collection(_) => {
1410 return;
1412 }
1413 };
1414
1415 let availability = *use_.availability();
1416 let mut router_builder = router
1417 .with_porcelain_with_default(porcelain_type)
1418 .availability(availability)
1419 .target(&component)
1420 .error_info(use_)
1421 .error_reporter(error_reporter);
1422 if let cm_rust::UseDecl::Directory(decl) = use_ {
1423 router_builder = router_builder
1424 .rights(Some(decl.rights.into()))
1425 .subdir(decl.subdir.clone().into())
1426 .inherit_rights(false);
1427 }
1428 if let cm_rust::UseDecl::Service(_) = use_ {
1429 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(false);
1430 }
1431 let router = router_builder.build();
1432
1433 match use_ {
1434 cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
1435 numbered_handle: Some(numbered_handle),
1436 ..
1437 }) => {
1438 let numbered_handle = Name::from(*numbered_handle);
1439 if let Err(e) =
1440 program_input.numbered_handles().insert_capability(&numbered_handle, router.into())
1441 {
1442 warn!("failed to insert {} in program input dict: {e:?}", numbered_handle)
1443 }
1444 }
1445 cm_rust::UseDecl::Runner(_) => {
1446 assert!(program_input.runner().is_none(), "component can't use multiple runners");
1447 program_input.set_runner(router.into());
1448 }
1449 _ => {
1450 if let Err(e) =
1451 program_input.namespace().insert_capability(use_.path().unwrap(), router.into())
1452 {
1453 warn!("failed to insert {} in program input dict: {e:?}", use_.path().unwrap())
1454 }
1455 }
1456 }
1457}
1458
1459fn extend_dict_with_dictionary_use<C: ComponentInstanceInterface + 'static>(
1460 component: &Arc<C>,
1461 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1462 component_input: &ComponentInput,
1463 program_input: &ProgramInput,
1464 program_output_dict: &Dict,
1465 framework_router: &Router<Dict>,
1466 capability_sourced_capabilities_dict: &Dict,
1467 use_bundle: Vec<&cm_rust::UseDecl>,
1468 error_reporter: impl ErrorReporter,
1469) {
1470 let path = use_bundle[0].path().unwrap();
1471 let mut dictionary_routers = vec![];
1472 for use_ in use_bundle.iter() {
1473 let dict_for_used_router = ProgramInput::new(Dict::new(), None, Dict::new());
1474 extend_dict_with_use::<Dict, _>(
1475 component,
1476 child_component_output_dictionary_routers,
1477 component_input,
1478 &dict_for_used_router,
1479 program_output_dict,
1480 framework_router,
1481 capability_sourced_capabilities_dict,
1482 use_,
1483 error_reporter.clone(),
1484 );
1485 let dictionary_router = match dict_for_used_router.namespace().get_capability(path) {
1486 Some(Capability::DictionaryRouter(router)) => router,
1487 other_value => panic!("unexpected dictionary get result: {other_value:?}"),
1488 };
1489 dictionary_routers.push(dictionary_router);
1490 }
1491 let original_dictionary = match program_input.namespace().get_capability(path) {
1492 Some(Capability::Dictionary(dictionary)) => dictionary,
1493 _ => Dict::new(),
1494 };
1495 let router = UseDictionaryRouter::new(
1496 path.clone(),
1497 component.moniker().clone(),
1498 original_dictionary,
1499 dictionary_routers,
1500 CapabilitySource::Component(ComponentSource {
1501 capability: ComponentCapability::Use_((*use_bundle.first().unwrap()).clone()),
1502 moniker: component.moniker().clone(),
1503 }),
1504 );
1505 match program_input.namespace().insert_capability(path, router.into()) {
1506 Ok(()) => (),
1507 Err(_e) => {
1508 }
1512 }
1513}
1514
1515fn use_from_parent_router<T>(
1517 component_input: &ComponentInput,
1518 source_path: impl IterablePath + 'static + Debug,
1519 moniker: &Moniker,
1520) -> Router<T>
1521where
1522 T: CapabilityBound + Clone,
1523 Router<T>: TryFrom<Capability>,
1524{
1525 let err = if moniker == &Moniker::root() {
1526 RoutingError::register_from_component_manager_not_found(
1527 source_path.iter_segments().join("/"),
1528 )
1529 } else {
1530 RoutingError::use_from_parent_not_found(moniker, source_path.iter_segments().join("/"))
1531 };
1532 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1533}
1534
1535fn is_supported_offer(offer: &cm_rust::OfferDecl) -> bool {
1536 matches!(
1537 offer,
1538 cm_rust::OfferDecl::Config(_)
1539 | cm_rust::OfferDecl::Protocol(_)
1540 | cm_rust::OfferDecl::Dictionary(_)
1541 | cm_rust::OfferDecl::Directory(_)
1542 | cm_rust::OfferDecl::Runner(_)
1543 | cm_rust::OfferDecl::Resolver(_)
1544 | cm_rust::OfferDecl::Service(_)
1545 | cm_rust::OfferDecl::EventStream(_)
1546 )
1547}
1548
1549fn extend_dict_with_offer<T, C: ComponentInstanceInterface + 'static>(
1550 component: &Arc<C>,
1551 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1552 component_input: &ComponentInput,
1553 program_output_dict: &Dict,
1554 framework_router: &Router<Dict>,
1555 capability_sourced_capabilities_dict: &Dict,
1556 offer: &cm_rust::OfferDecl,
1557 target_dict: &Dict,
1558 error_reporter: impl ErrorReporter,
1559) where
1560 T: CapabilityBound + Clone,
1561 Router<T>: TryFrom<Capability> + Into<Capability> + WithServiceRenamesAndFilter,
1562{
1563 assert!(is_supported_offer(offer), "{offer:?}");
1564
1565 let source_path = offer.source_path();
1566 let target_name = offer.target_name();
1567 let porcelain_type = CapabilityTypeName::from(offer);
1568 let router: Router<T> = match offer.source() {
1569 cm_rust::OfferSource::Parent => {
1570 let err = if component.moniker() == &Moniker::root() {
1571 RoutingError::register_from_component_manager_not_found(
1572 offer.source_name().to_string(),
1573 )
1574 } else {
1575 RoutingError::offer_from_parent_not_found(
1576 &component.moniker(),
1577 source_path.iter_segments().join("/"),
1578 )
1579 };
1580 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1581 }
1582 cm_rust::OfferSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1583 &source_path,
1584 RoutingError::offer_from_self_not_found(
1585 &component.moniker(),
1586 source_path.iter_segments().join("/"),
1587 ),
1588 ),
1589 cm_rust::OfferSource::Child(child_ref) => {
1590 let child_name: ChildName = child_ref.clone().try_into().expect("invalid child ref");
1591 match child_component_output_dictionary_routers.get(&child_name) {
1592 None => Router::<T>::new_error(RoutingError::offer_from_child_instance_not_found(
1593 &child_name,
1594 &component.moniker(),
1595 source_path.iter_segments().join("/"),
1596 )),
1597 Some(child_component_output) => child_component_output.clone().lazy_get(
1598 source_path.to_owned(),
1599 RoutingError::offer_from_child_expose_not_found(
1600 &child_name,
1601 &component.moniker(),
1602 offer.source_name().clone(),
1603 ),
1604 ),
1605 }
1606 }
1607 cm_rust::OfferSource::Framework => {
1608 if offer.is_from_dictionary() {
1609 warn!(
1610 "routing from framework with dictionary path is not supported: {source_path}"
1611 );
1612 return;
1613 }
1614 query_framework_router_or_not_found(framework_router, &source_path, component)
1615 }
1616 cm_rust::OfferSource::Capability(capability_name) => {
1617 let err = RoutingError::capability_from_capability_not_found(
1618 &component.moniker(),
1619 capability_name.as_str().to_string(),
1620 );
1621 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1622 || source_path.iter_segments().join("/")
1623 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1624 {
1625 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1626 } else {
1627 Router::<T>::new_error(err)
1628 }
1629 }
1630 cm_rust::OfferSource::Void => UnavailableRouter::new_from_offer(offer, component),
1631 cm_rust::OfferSource::Collection(_collection_name) => {
1632 return;
1637 }
1638 };
1639
1640 let availability = *offer.availability();
1641 let mut router_builder = router
1642 .with_porcelain_with_default(porcelain_type)
1643 .availability(availability)
1644 .target(component)
1645 .error_info(offer)
1646 .error_reporter(error_reporter);
1647 if let cm_rust::OfferDecl::Directory(decl) = offer {
1648 router_builder = router_builder
1658 .rights(decl.rights.clone().map(Into::into))
1659 .subdir(decl.subdir.clone().into())
1660 .inherit_rights(true);
1661 }
1662 if let cm_rust::OfferDecl::Service(_) = offer {
1663 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1664 }
1665 if let cm_rust::OfferDecl::EventStream(offer_event_stream) = offer {
1666 if let Some(scope) = &offer_event_stream.scope {
1667 router_builder =
1668 router_builder.event_stream_route_metadata(finternal::EventStreamRouteMetadata {
1669 scope_moniker: Some(component.moniker().to_string()),
1670 scope: Some(scope.clone().native_into_fidl()),
1671 ..Default::default()
1672 });
1673 }
1674 }
1675 let router = router_builder.build().with_service_renames_and_filter(offer.clone());
1676 match target_dict.insert_capability(target_name, router.into()) {
1677 Ok(()) => (),
1678 Err(e) => warn!("failed to insert {target_name} into target dict: {e:?}"),
1679 }
1680}
1681
1682fn query_framework_router_or_not_found<T, C>(
1683 router: &Router<Dict>,
1684 path: &BorrowedSeparatedPath<'_>,
1685 component: &Arc<C>,
1686) -> Router<T>
1687where
1688 T: CapabilityBound,
1689 Router<T>: TryFrom<Capability>,
1690 C: ComponentInstanceInterface + 'static,
1691{
1692 let request = Request { metadata: Dict::new() };
1693 let dict: Result<RouterResponse<Dict>, RouterError> = router
1694 .route(Some(request), false, component.as_weak().into())
1695 .now_or_never()
1696 .expect("failed to now_or_never");
1697 let dict = match dict {
1698 Ok(RouterResponse::Capability(dict)) => dict,
1699 _ => Dict::new(),
1701 };
1702 dict.get_router_or_not_found::<T>(
1705 path,
1706 RoutingError::capability_from_framework_not_found(
1707 &component.moniker(),
1708 path.iter_segments().join("/"),
1709 ),
1710 )
1711}
1712
1713pub fn is_supported_expose(expose: &cm_rust::ExposeDecl) -> bool {
1714 matches!(
1715 expose,
1716 cm_rust::ExposeDecl::Config(_)
1717 | cm_rust::ExposeDecl::Protocol(_)
1718 | cm_rust::ExposeDecl::Dictionary(_)
1719 | cm_rust::ExposeDecl::Directory(_)
1720 | cm_rust::ExposeDecl::Runner(_)
1721 | cm_rust::ExposeDecl::Resolver(_)
1722 | cm_rust::ExposeDecl::Service(_)
1723 )
1724}
1725
1726fn extend_dict_with_expose<T, C: ComponentInstanceInterface + 'static>(
1727 component: &Arc<C>,
1728 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1729 program_output_dict: &Dict,
1730 framework_router: &Router<Dict>,
1731 capability_sourced_capabilities_dict: &Dict,
1732 expose: &cm_rust::ExposeDecl,
1733 target_component_output: &ComponentOutput,
1734 error_reporter: impl ErrorReporter,
1735) where
1736 T: CapabilityBound + Clone,
1737 Router<T>: TryFrom<Capability> + Into<Capability>,
1738{
1739 assert!(is_supported_expose(expose), "{expose:?}");
1740
1741 let target_dict = match expose.target() {
1742 cm_rust::ExposeTarget::Parent => target_component_output.capabilities(),
1743 cm_rust::ExposeTarget::Framework => target_component_output.framework(),
1744 };
1745 let source_path = expose.source_path();
1746 let target_name = expose.target_name();
1747
1748 let porcelain_type = CapabilityTypeName::from(expose);
1749 let router: Router<T> = match expose.source() {
1750 cm_rust::ExposeSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1751 &source_path,
1752 RoutingError::expose_from_self_not_found(
1753 &component.moniker(),
1754 source_path.iter_segments().join("/"),
1755 ),
1756 ),
1757 cm_rust::ExposeSource::Child(child_name) => {
1758 let child_name = ChildName::parse(child_name).expect("invalid static child name");
1759 if let Some(child_component_output) =
1760 child_component_output_dictionary_routers.get(&child_name)
1761 {
1762 child_component_output.clone().lazy_get(
1763 source_path.to_owned(),
1764 RoutingError::expose_from_child_expose_not_found(
1765 &child_name,
1766 &component.moniker(),
1767 expose.source_name().clone(),
1768 ),
1769 )
1770 } else {
1771 Router::<T>::new_error(RoutingError::expose_from_child_instance_not_found(
1772 &child_name,
1773 &component.moniker(),
1774 expose.source_name().clone(),
1775 ))
1776 }
1777 }
1778 cm_rust::ExposeSource::Framework => {
1779 if expose.is_from_dictionary() {
1780 warn!(
1781 "routing from framework with dictionary path is not supported: {source_path}"
1782 );
1783 return;
1784 }
1785 query_framework_router_or_not_found(framework_router, &source_path, component)
1786 }
1787 cm_rust::ExposeSource::Capability(capability_name) => {
1788 let err = RoutingError::capability_from_capability_not_found(
1789 &component.moniker(),
1790 capability_name.as_str().to_string(),
1791 );
1792 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1793 || source_path.iter_segments().join("/")
1794 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1795 {
1796 capability_sourced_capabilities_dict
1797 .clone()
1798 .get_router_or_not_found::<T>(&capability_name, err)
1799 } else {
1800 Router::<T>::new_error(err)
1801 }
1802 }
1803 cm_rust::ExposeSource::Void => UnavailableRouter::new_from_expose(expose, component),
1804 cm_rust::ExposeSource::Collection(_name) => return,
1809 };
1810 let availability = *expose.availability();
1811 let mut router_builder = router
1812 .with_porcelain_with_default(porcelain_type)
1813 .availability(availability)
1814 .target(component)
1815 .error_info(expose)
1816 .error_reporter(error_reporter);
1817 if let cm_rust::ExposeDecl::Directory(decl) = expose {
1818 router_builder = router_builder
1819 .rights(decl.rights.clone().map(Into::into))
1820 .subdir(decl.subdir.clone().into())
1821 .inherit_rights(true);
1822 };
1823 if let cm_rust::ExposeDecl::Service(_) = expose {
1824 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1825 };
1826 match target_dict.insert_capability(target_name, router_builder.build().into()) {
1827 Ok(()) => (),
1828 Err(e) => warn!("failed to insert {target_name} into target_dict: {e:?}"),
1829 }
1830}
1831
1832struct UnavailableRouter<C: ComponentInstanceInterface> {
1833 capability: InternalCapability,
1834 component: WeakComponentInstanceInterface<C>,
1835}
1836
1837impl<C: ComponentInstanceInterface + 'static> UnavailableRouter<C> {
1838 fn new<T: CapabilityBound>(capability: InternalCapability, component: &Arc<C>) -> Router<T> {
1839 Router::<T>::new(Self { capability, component: component.as_weak() })
1840 }
1841
1842 fn new_from_offer<T: CapabilityBound>(offer: &OfferDecl, component: &Arc<C>) -> Router<T> {
1843 let name = offer.source_name().clone();
1844 let capability = match offer {
1845 OfferDecl::Service(_) => InternalCapability::Service(name),
1846 OfferDecl::Protocol(_) => InternalCapability::Protocol(name),
1847 OfferDecl::Directory(_) => InternalCapability::Directory(name),
1848 OfferDecl::Storage(_) => InternalCapability::Storage(name),
1849 OfferDecl::Runner(_) => InternalCapability::Runner(name),
1850 OfferDecl::Resolver(_) => InternalCapability::Resolver(name),
1851 OfferDecl::EventStream(_) => InternalCapability::EventStream(name),
1852 OfferDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1853 OfferDecl::Config(_) => InternalCapability::Config(name),
1854 };
1855 Self::new(capability, component)
1856 }
1857
1858 fn new_from_expose<T: CapabilityBound>(expose: &ExposeDecl, component: &Arc<C>) -> Router<T> {
1859 let name = expose.source_name().clone();
1860 let capability = match expose {
1861 ExposeDecl::Service(_) => InternalCapability::Service(name),
1862 ExposeDecl::Protocol(_) => InternalCapability::Protocol(name),
1863 ExposeDecl::Directory(_) => InternalCapability::Directory(name),
1864 ExposeDecl::Runner(_) => InternalCapability::Runner(name),
1865 ExposeDecl::Resolver(_) => InternalCapability::Resolver(name),
1866 ExposeDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1867 ExposeDecl::Config(_) => InternalCapability::Config(name),
1868 };
1869 Self::new(capability, component)
1870 }
1871}
1872
1873#[async_trait]
1874impl<T: CapabilityBound, C: ComponentInstanceInterface + 'static> Routable<T>
1875 for UnavailableRouter<C>
1876{
1877 async fn route(
1878 &self,
1879 request: Option<Request>,
1880 debug: bool,
1881 _target: WeakInstanceToken,
1882 ) -> Result<RouterResponse<T>, RouterError> {
1883 if debug {
1884 let data = CapabilitySource::Void(VoidSource {
1885 capability: self.capability.clone(),
1886 moniker: self.component.moniker.clone(),
1887 })
1888 .try_into()
1889 .expect("failed to convert capability source to Data");
1890 return Ok(RouterResponse::<T>::Debug(data));
1891 }
1892 let request = request.ok_or_else(|| RouterError::InvalidArgs)?;
1893 let availability = request.metadata.get_metadata().ok_or(RouterError::InvalidArgs)?;
1894 match availability {
1895 cm_rust::Availability::Required | cm_rust::Availability::SameAsTarget => {
1896 Err(RoutingError::SourceCapabilityIsVoid {
1897 moniker: self.component.moniker.clone(),
1898 }
1899 .into())
1900 }
1901 cm_rust::Availability::Optional | cm_rust::Availability::Transitional => {
1902 Ok(RouterResponse::Unavailable)
1903 }
1904 }
1905 }
1906}