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