fuchsia_component_server/lib.rs
1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Tools for providing Fuchsia services.
6
7#![deny(missing_docs)]
8
9use anyhow::Error;
10use fidl::endpoints::{
11 DiscoverableProtocolMarker, Proxy as _, RequestStream, ServerEnd, ServiceMarker, ServiceRequest,
12};
13use fuchsia_component_client::connect_channel_to_protocol;
14use futures::channel::mpsc;
15use futures::future::BoxFuture;
16use futures::{FutureExt, Stream, StreamExt};
17use log::warn;
18use pin_project::pin_project;
19use std::marker::PhantomData;
20use std::pin::Pin;
21use std::sync::Arc;
22use std::task::{Context, Poll};
23use thiserror::Error;
24use vfs::directory::entry::DirectoryEntry;
25use vfs::directory::helper::DirectlyMutable;
26use vfs::directory::immutable::Simple as PseudoDir;
27use vfs::execution_scope::ExecutionScope;
28use vfs::file::vmo::VmoFile;
29use vfs::name::Name;
30use vfs::remote::remote_dir;
31use vfs::service::endpoint;
32use zx::MonotonicDuration;
33use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
34
35mod service;
36pub use service::{
37 FidlService, FidlServiceMember, FidlServiceServerConnector, Service, ServiceObj,
38 ServiceObjLocal, ServiceObjTrait,
39};
40mod until_stalled;
41pub use until_stalled::{Item, StallableServiceFs};
42
43/// A filesystem which connects clients to services.
44///
45/// This type implements the `Stream` trait and will yield the values
46/// returned from calling `Service::connect` on the services it hosts.
47///
48/// This can be used to, for example, yield streams of channels, request
49/// streams, futures to run, or any other value that should be processed
50/// as the result of a request.
51#[must_use]
52#[pin_project]
53pub struct ServiceFs<ServiceObjTy: ServiceObjTrait> {
54 // The execution scope for the backing VFS.
55 scope: ExecutionScope,
56
57 // The root directory.
58 dir: Arc<PseudoDir>,
59
60 // New connections are sent via an mpsc. The tuple is (index, channel) where index is the index
61 // into the `services` member.
62 new_connection_sender: mpsc::UnboundedSender<(usize, zx::Channel)>,
63 new_connection_receiver: mpsc::UnboundedReceiver<(usize, zx::Channel)>,
64
65 // A collection of objects that are able to handle new connections and convert them into a
66 // stream of ServiceObjTy::Output requests. There will be one for each service in the
67 // filesystem (irrespective of its place in the hierarchy).
68 services: Vec<ServiceObjTy>,
69
70 // A future that completes when the VFS no longer has any connections. These connections are
71 // distinct from connections that might be to services or remotes within this filesystem.
72 shutdown: BoxFuture<'static, ()>,
73
74 // The filesystem does not start servicing any requests until ServiceFs is first polled. This
75 // preserves behaviour of ServiceFs from when it didn't use the Rust VFS, and is relied upon in
76 // some cases. The queue is used until first polled. After that, `channel_queue` will be None
77 // and requests to service channels will be actioned immediately (potentially on different
78 // threads depending on the executor).
79 channel_queue: Option<Vec<fidl::endpoints::ServerEnd<fio::DirectoryMarker>>>,
80}
81
82impl<'a, Output: 'a> ServiceFs<ServiceObjLocal<'a, Output>> {
83 /// Create a new `ServiceFs` that is singlethreaded-only and does not
84 /// require services to implement `Send`.
85 pub fn new_local() -> Self {
86 Self::new_impl()
87 }
88}
89
90impl<'a, Output: 'a> ServiceFs<ServiceObj<'a, Output>> {
91 /// Create a new `ServiceFs` that is multithreaded-capable and requires
92 /// services to implement `Send`.
93 pub fn new() -> Self {
94 Self::new_impl()
95 }
96}
97
98/// A directory within a `ServiceFs`.
99///
100/// Services and subdirectories can be added to it.
101pub struct ServiceFsDir<'a, ServiceObjTy: ServiceObjTrait> {
102 fs: &'a mut ServiceFs<ServiceObjTy>,
103 dir: Arc<PseudoDir>,
104}
105
106/// A `Service` implementation that proxies requests
107/// to the outside environment.
108///
109/// Not intended for direct use. Use the `add_proxy_service`
110/// function instead.
111#[doc(hidden)]
112pub struct Proxy<P, O>(PhantomData<(P, fn() -> O)>);
113
114impl<P: DiscoverableProtocolMarker, O> Service for Proxy<P, O> {
115 type Output = O;
116 fn connect(&mut self, channel: zx::Channel) -> Option<O> {
117 if let Err(e) = connect_channel_to_protocol::<P>(channel) {
118 eprintln!("failed to proxy request to {}: {:?}", P::PROTOCOL_NAME, e);
119 }
120 None
121 }
122}
123
124/// A `Service` implementation that proxies requests to the given component.
125///
126/// Not intended for direct use. Use the `add_proxy_service_to` function instead.
127#[doc(hidden)]
128pub struct ProxyTo<P, O> {
129 directory_request: Arc<fidl::endpoints::ClientEnd<fio::DirectoryMarker>>,
130 _phantom: PhantomData<(P, fn() -> O)>,
131}
132
133impl<P: DiscoverableProtocolMarker, O> Service for ProxyTo<P, O> {
134 type Output = O;
135 fn connect(&mut self, channel: zx::Channel) -> Option<O> {
136 if let Err(e) =
137 fdio::service_connect_at(self.directory_request.channel(), P::PROTOCOL_NAME, channel)
138 {
139 eprintln!("failed to proxy request to {}: {:?}", P::PROTOCOL_NAME, e);
140 }
141 None
142 }
143}
144
145// Not part of a trait so that clients won't have to import a trait
146// in order to call these functions.
147macro_rules! add_functions {
148 () => {
149 /// Adds a service connector to the directory.
150 ///
151 /// ```rust
152 /// let mut fs = ServiceFs::new_local();
153 /// fs
154 /// .add_service_connector(|server_end: ServerEnd<EchoMarker>| {
155 /// connect_channel_to_protocol::<EchoMarker>(
156 /// server_end.into_channel(),
157 /// )
158 /// })
159 /// .add_service_connector(|server_end: ServerEnd<CustomMarker>| {
160 /// connect_channel_to_protocol::<CustomMarker>(
161 /// server_end.into_channel(),
162 /// )
163 /// })
164 /// .take_and_serve_directory_handle()?;
165 /// ```
166 ///
167 /// The FIDL service will be hosted at the name provided by the
168 /// `[Discoverable]` annotation in the FIDL source.
169 pub fn add_service_connector<F, P>(&mut self, service: F) -> &mut Self
170 where
171 F: FnMut(ServerEnd<P>) -> ServiceObjTy::Output,
172 P: DiscoverableProtocolMarker,
173 FidlServiceServerConnector<F, P, ServiceObjTy::Output>: Into<ServiceObjTy>,
174 {
175 self.add_service_at(P::PROTOCOL_NAME, FidlServiceServerConnector::from(service))
176 }
177
178 /// Adds a service to the directory at the given path.
179 ///
180 /// The path must be a single component containing no `/` characters.
181 ///
182 /// Panics if any node has already been added at the given path.
183 pub fn add_service_at(
184 &mut self,
185 path: impl Into<String>,
186 service: impl Into<ServiceObjTy>,
187 ) -> &mut Self {
188 let index = self.fs().services.len();
189 self.fs().services.push(service.into());
190 let sender = self.fs().new_connection_sender.clone();
191 self.add_entry_at(
192 path,
193 endpoint(move |_, channel| {
194 // It's possible for this send to fail in the case where ServiceFs has been
195 // dropped. When that happens, ServiceFs will drop ExecutionScope which
196 // contains the RemoteHandle for this task which will then cause this task to be
197 // dropped but not necessarily immediately. This will only occur when ServiceFs
198 // has been dropped, so it's safe to ignore the error here.
199 let _ = sender.unbounded_send((index, channel.into()));
200 }),
201 )
202 }
203
204 /// Adds a FIDL service to the directory.
205 ///
206 /// `service` is a closure that accepts a `RequestStream`.
207 /// Each service being served must return an instance of the same type
208 /// (`ServiceObjTy::Output`). This is necessary in order to multiplex
209 /// multiple services over the same dispatcher code. The typical way
210 /// to do this is to create an `enum` with variants for each service
211 /// you want to serve.
212 ///
213 /// ```rust
214 /// enum MyServices {
215 /// EchoServer(EchoRequestStream),
216 /// CustomServer(CustomRequestStream),
217 /// // ...
218 /// }
219 /// ```
220 ///
221 /// The constructor for a variant of the `MyServices` enum can be passed
222 /// as the `service` parameter.
223 ///
224 /// ```rust
225 /// let mut fs = ServiceFs::new_local();
226 /// fs
227 /// .add_fidl_service(MyServices::EchoServer)
228 /// .add_fidl_service(MyServices::CustomServer)
229 /// .take_and_serve_directory_handle()?;
230 /// ```
231 ///
232 /// `ServiceFs` can now be treated as a `Stream` of type `MyServices`.
233 ///
234 /// ```rust
235 /// const MAX_CONCURRENT: usize = 10_000;
236 /// fs.for_each_concurrent(MAX_CONCURRENT, |request: MyServices| {
237 /// match request {
238 /// MyServices::EchoServer(request) => handle_echo(request),
239 /// MyServices::CustomServer(request) => handle_custom(request),
240 /// }
241 /// }).await;
242 /// ```
243 ///
244 /// The FIDL service will be hosted at the name provided by the
245 /// `[Discoverable]` annotation in the FIDL source.
246 pub fn add_fidl_service<F, RS>(&mut self, service: F) -> &mut Self
247 where
248 F: FnMut(RS) -> ServiceObjTy::Output,
249 RS: RequestStream,
250 RS::Protocol: DiscoverableProtocolMarker,
251 FidlService<F, RS, ServiceObjTy::Output>: Into<ServiceObjTy>,
252 {
253 self.add_fidl_service_at(RS::Protocol::PROTOCOL_NAME, service)
254 }
255
256 /// Adds a FIDL service to the directory at the given path.
257 ///
258 /// The path must be a single component containing no `/` characters.
259 ///
260 /// See [`add_fidl_service`](#method.add_fidl_service) for details.
261 pub fn add_fidl_service_at<F, RS>(
262 &mut self,
263 path: impl Into<String>,
264 service: F,
265 ) -> &mut Self
266 where
267 F: FnMut(RS) -> ServiceObjTy::Output,
268 RS: RequestStream,
269 RS::Protocol: DiscoverableProtocolMarker,
270 FidlService<F, RS, ServiceObjTy::Output>: Into<ServiceObjTy>,
271 {
272 self.add_service_at(path, FidlService::from(service))
273 }
274
275 /// Adds a named instance of a FIDL service to the directory.
276 ///
277 /// The FIDL service will be hosted at `[SERVICE_NAME]/[instance]/` where `SERVICE_NAME` is
278 /// constructed from the FIDL library path and the name of the FIDL service.
279 ///
280 /// The `instance` must be a single component containing no `/` characters.
281 ///
282 /// # Example
283 ///
284 /// For the following FIDL definition,
285 /// ```fidl
286 /// library lib.foo;
287 ///
288 /// service Bar {
289 /// ...
290 /// }
291 /// ```
292 ///
293 /// The `SERVICE_NAME` of FIDL Service `Bar` would be `lib.foo.Bar`.
294 pub fn add_fidl_service_instance<F, SR>(
295 &mut self,
296 instance: impl Into<String>,
297 service: F,
298 ) -> &mut Self
299 where
300 F: Fn(SR) -> ServiceObjTy::Output,
301 F: Clone,
302 SR: ServiceRequest,
303 FidlServiceMember<F, SR, ServiceObjTy::Output>: Into<ServiceObjTy>,
304 {
305 self.add_fidl_service_instance_at(SR::Service::SERVICE_NAME, instance, service)
306 }
307
308 /// Adds a named instance of a FIDL service to the directory at the given path.
309 ///
310 /// The FIDL service will be hosted at `[path]/[instance]/`.
311 ///
312 /// The `path` and `instance` must be single components containing no `/` characters.
313 pub fn add_fidl_service_instance_at<F, SR>(
314 &mut self,
315 path: impl Into<String>,
316 instance: impl Into<String>,
317 service: F,
318 ) -> &mut Self
319 where
320 F: Fn(SR) -> ServiceObjTy::Output,
321 F: Clone,
322 SR: ServiceRequest,
323 FidlServiceMember<F, SR, ServiceObjTy::Output>: Into<ServiceObjTy>,
324 {
325 // Create the service directory, with an instance subdirectory.
326 let mut dir = self.dir(path);
327 let mut dir = dir.dir(instance);
328
329 // Attach member protocols under the instance directory.
330 for member in SR::member_names() {
331 dir.add_service_at(*member, FidlServiceMember::new(service.clone(), member));
332 }
333 self
334 }
335
336 /// Adds a service that proxies requests to the current environment.
337 // NOTE: we'd like to be able to remove the type parameter `O` here,
338 // but unfortunately the bound `ServiceObjTy: From<Proxy<P, ServiceObjTy::Output>>`
339 // makes type checking angry.
340 pub fn add_proxy_service<P: DiscoverableProtocolMarker, O>(&mut self) -> &mut Self
341 where
342 ServiceObjTy: From<Proxy<P, O>>,
343 ServiceObjTy: ServiceObjTrait<Output = O>,
344 {
345 self.add_service_at(P::PROTOCOL_NAME, Proxy::<P, ServiceObjTy::Output>(PhantomData))
346 }
347
348 /// Adds a service that proxies requests to the given component.
349 // NOTE: we'd like to be able to remove the type parameter `O` here,
350 // but unfortunately the bound `ServiceObjTy: From<Proxy<P, ServiceObjTy::Output>>`
351 // makes type checking angry.
352 pub fn add_proxy_service_to<P: DiscoverableProtocolMarker, O>(
353 &mut self,
354 directory_request: Arc<fidl::endpoints::ClientEnd<fio::DirectoryMarker>>,
355 ) -> &mut Self
356 where
357 ServiceObjTy: From<ProxyTo<P, O>>,
358 ServiceObjTy: ServiceObjTrait<Output = O>,
359 {
360 self.add_service_at(
361 P::PROTOCOL_NAME,
362 ProxyTo::<P, ServiceObjTy::Output> { directory_request, _phantom: PhantomData },
363 )
364 }
365
366 /// Adds a VMO file to the directory at the given path.
367 ///
368 /// The path must be a single component containing no `/` characters. The vmo should have
369 /// content size set as required.
370 ///
371 /// Panics if any node has already been added at the given path.
372 pub fn add_vmo_file_at(&mut self, path: impl Into<String>, vmo: zx::Vmo) -> &mut Self {
373 self.add_entry_at(path, VmoFile::new(vmo))
374 }
375
376 /// Adds an entry to the directory at the given path.
377 ///
378 /// The path must be a single component.
379 /// The path must be a valid `fuchsia.io` [`Name`].
380 ///
381 /// Panics if any node has already been added at the given path.
382 pub fn add_entry_at(
383 &mut self,
384 path: impl Into<String>,
385 entry: Arc<dyn DirectoryEntry>,
386 ) -> &mut Self {
387 let path: String = path.into();
388 let name: Name = path.try_into().expect("Invalid path");
389 // This will fail if the name is invalid or already exists.
390 self.dir.add_entry_impl(name, entry, false).expect("Unable to add entry");
391 self
392 }
393
394 /// Returns a reference to the subdirectory at the given path,
395 /// creating one if none exists.
396 ///
397 /// The path must be a single component.
398 /// The path must be a valid `fuchsia.io` [`Name`].
399 ///
400 /// Panics if a service has already been added at the given path.
401 pub fn dir(&mut self, path: impl Into<String>) -> ServiceFsDir<'_, ServiceObjTy> {
402 let path: String = path.into();
403 let name: Name = path.try_into().expect("Invalid path");
404 let dir = Arc::downcast(self.dir.get_or_insert(name, new_simple_dir).into_any())
405 .unwrap_or_else(|_| panic!("Not a directory"));
406 ServiceFsDir { fs: self.fs(), dir }
407 }
408
409 /// Adds a new remote directory served over the given DirectoryProxy.
410 ///
411 /// The name must be a valid `fuchsia.io` [`Name`].
412 pub fn add_remote(
413 &mut self,
414 name: impl Into<String>,
415 proxy: fio::DirectoryProxy,
416 ) -> &mut Self {
417 let name: String = name.into();
418 let name: Name = name.try_into().expect("Invalid path");
419 self.dir.add_entry_impl(name, remote_dir(proxy), false).expect("Unable to add entry");
420 self
421 }
422
423 /// Adds a FIDL protocol to the directory.
424 ///
425 /// The FIDL protocol will be hosted at the name provided by the
426 /// `Discoverable` annotation in the FIDL source.
427 pub fn add_fidl_next_protocol<P, H>(&mut self, handler: H) -> &mut Self
428 where
429 P: ::fidl_next::Protocol<zx::Channel>
430 + ::fidl_next::Discoverable
431 + ::fidl_next::DispatchServerMessage<H, zx::Channel>,
432 H: Clone + Send + Sync + 'static,
433 {
434 self.dir
435 .add_entry_impl(
436 String::from(P::PROTOCOL_NAME).try_into().expect("Invalid path"),
437 endpoint(move |_, channel| {
438 let handler = handler.clone();
439 fasync::Task::spawn(async move {
440 // TODO: logging?
441 let server_end = ::fidl_next::ServerEnd::<P, zx::Channel>::from_untyped(
442 channel.into_zx_channel(),
443 );
444 ::fidl_next::Server::new(server_end)
445 .run(handler)
446 .await
447 .expect("Protocol service was terminated ");
448 })
449 .detach();
450 }),
451 false,
452 )
453 .expect("Unable to add entry");
454 self
455 }
456
457 /// Adds a FIDL service to the directory.
458 ///
459 /// The FIDL service will be hosted at the name provided by the
460 /// `Discoverable` annotation in the FIDL source.
461 pub fn add_fidl_next_service_instance<S, H>(
462 &mut self,
463 instance_name: impl Into<String>,
464 handler: H,
465 ) -> &mut Self
466 where
467 S: ::fidl_next::DiscoverableService
468 + ::fidl_next::DispatchServiceHandler<H, zx::Channel>
469 + 'static,
470 H: Send + Sync + 'static,
471 {
472 // Create the service directory, with an instance subdirectory
473 let mut dir = self.dir(S::SERVICE_NAME);
474 let mut dir = dir.dir(instance_name);
475
476 let handler = std::sync::Arc::new(
477 ::fidl_next::ServiceHandlerAdapter::<S, H>::from_untyped(handler),
478 );
479
480 // Attach member protocols under the instance directory
481 for member_name in S::MEMBER_NAMES {
482 let handler = handler.clone();
483 dir.add_entry_at(
484 *member_name,
485 endpoint(move |_, channel| {
486 ::fidl_next::protocol::ServiceHandler::on_connection(
487 &*handler,
488 member_name,
489 channel.into_zx_channel(),
490 );
491 }),
492 );
493 }
494 self
495 }
496 };
497}
498
499impl<ServiceObjTy: ServiceObjTrait> ServiceFsDir<'_, ServiceObjTy> {
500 fn fs(&mut self) -> &mut ServiceFs<ServiceObjTy> {
501 self.fs
502 }
503
504 add_functions!();
505}
506
507impl<ServiceObjTy: ServiceObjTrait> ServiceFs<ServiceObjTy> {
508 fn new_impl() -> Self {
509 let (new_connection_sender, new_connection_receiver) = mpsc::unbounded();
510 let scope = ExecutionScope::new();
511 let dir = new_simple_dir();
512 Self {
513 scope: scope.clone(),
514 dir,
515 new_connection_sender,
516 new_connection_receiver,
517 services: Vec::new(),
518 shutdown: async move { scope.wait().await }.boxed(),
519 channel_queue: Some(Vec::new()),
520 }
521 }
522
523 fn fs(&mut self) -> &mut ServiceFs<ServiceObjTy> {
524 self
525 }
526
527 /// Get a reference to the root directory as a `ServiceFsDir`.
528 ///
529 /// This can be useful when writing code which hosts some set of services on
530 /// a directory and wants to be agnostic to whether that directory
531 /// is the root `ServiceFs` or a subdirectory.
532 ///
533 /// Such a function can take an `&mut ServiceFsDir<...>` as an argument,
534 /// allowing callers to provide either a subdirectory or `fs.root_dir()`.
535 pub fn root_dir(&mut self) -> ServiceFsDir<'_, ServiceObjTy> {
536 let dir = self.dir.clone();
537 ServiceFsDir { fs: self, dir }
538 }
539
540 add_functions!();
541
542 /// When a connection is first made to the `ServiceFs` in the absence of a parent connection,
543 /// it will be granted these rights.
544 const fn base_connection_flags() -> fio::Flags {
545 return fio::Flags::PROTOCOL_DIRECTORY
546 .union(fio::PERM_READABLE)
547 .union(fio::PERM_WRITABLE)
548 .union(fio::PERM_EXECUTABLE);
549 }
550
551 fn serve_connection_impl(&self, chan: fidl::endpoints::ServerEnd<fio::DirectoryMarker>) {
552 vfs::directory::serve_on(
553 self.dir.clone(),
554 Self::base_connection_flags(),
555 self.scope.clone(),
556 chan,
557 );
558 }
559
560 /// Creates a protocol connector that can access the capabilities exposed by this ServiceFs.
561 pub fn create_protocol_connector<O>(&mut self) -> Result<ProtocolConnector, Error>
562 where
563 ServiceObjTy: ServiceObjTrait<Output = O>,
564 {
565 let (directory_request, directory_server_end) = fidl::endpoints::create_endpoints();
566 self.serve_connection(directory_server_end)?;
567
568 Ok(ProtocolConnector { directory_request })
569 }
570}
571
572fn new_simple_dir() -> Arc<PseudoDir> {
573 let dir = PseudoDir::new();
574 dir.clone().set_not_found_handler(Box::new(move |path| {
575 warn!(
576 "ServiceFs received request to `{}` but has not been configured to serve this path.",
577 path
578 );
579 }));
580 dir
581}
582
583/// `ProtocolConnector` allows connecting to capabilities exposed by ServiceFs
584pub struct ProtocolConnector {
585 directory_request: fidl::endpoints::ClientEnd<fio::DirectoryMarker>,
586}
587
588impl ProtocolConnector {
589 /// Connect to a protocol provided by this environment.
590 #[inline]
591 pub fn connect_to_service<P: DiscoverableProtocolMarker>(&self) -> Result<P::Proxy, Error> {
592 self.connect_to_protocol::<P>()
593 }
594
595 /// Connect to a protocol provided by this environment.
596 #[inline]
597 pub fn connect_to_protocol<P: DiscoverableProtocolMarker>(&self) -> Result<P::Proxy, Error> {
598 let (client_channel, server_channel) = zx::Channel::create();
599 self.pass_to_protocol::<P>(server_channel)?;
600 Ok(P::Proxy::from_channel(fasync::Channel::from_channel(client_channel)))
601 }
602
603 /// Connect to a protocol by passing a channel for the server.
604 #[inline]
605 pub fn pass_to_protocol<P: DiscoverableProtocolMarker>(
606 &self,
607 server_channel: zx::Channel,
608 ) -> Result<(), Error> {
609 self.pass_to_named_protocol(P::PROTOCOL_NAME, server_channel)
610 }
611
612 /// Connect to a protocol by name.
613 #[inline]
614 pub fn pass_to_named_protocol(
615 &self,
616 protocol_name: &str,
617 server_channel: zx::Channel,
618 ) -> Result<(), Error> {
619 fdio::service_connect_at(self.directory_request.channel(), protocol_name, server_channel)?;
620 Ok(())
621 }
622}
623
624/// An error indicating the startup handle on which the FIDL server
625/// attempted to start was missing.
626#[derive(Debug, Error)]
627#[error("The startup handle on which the FIDL server attempted to start was missing.")]
628pub struct MissingStartupHandle;
629
630impl<ServiceObjTy: ServiceObjTrait> ServiceFs<ServiceObjTy> {
631 /// Removes the `DirectoryRequest` startup handle for the current
632 /// component and adds connects it to this `ServiceFs` as a client.
633 ///
634 /// Multiple calls to this function from the same component will
635 /// result in `Err(MissingStartupHandle)`.
636 pub fn take_and_serve_directory_handle(&mut self) -> Result<&mut Self, Error> {
637 let startup_handle = fuchsia_runtime::take_startup_handle(
638 fuchsia_runtime::HandleType::DirectoryRequest.into(),
639 )
640 .ok_or(MissingStartupHandle)?;
641
642 self.serve_connection(fidl::endpoints::ServerEnd::new(zx::Channel::from(startup_handle)))
643 }
644
645 /// Add a channel to serve this `ServiceFs` filesystem on. The `ServiceFs`
646 /// will continue to be provided over previously added channels, including
647 /// the one added if `take_and_serve_directory_handle` was called.
648 pub fn serve_connection(
649 &mut self,
650 chan: fidl::endpoints::ServerEnd<fio::DirectoryMarker>,
651 ) -> Result<&mut Self, Error> {
652 if let Some(channels) = &mut self.channel_queue {
653 channels.push(chan);
654 } else {
655 self.serve_connection_impl(chan);
656 }
657 Ok(self)
658 }
659
660 /// TODO(https://fxbug.dev/326626515): this is an experimental method to run a FIDL
661 /// directory connection until stalled, with the purpose to cleanly stop a component.
662 /// We'll expect to revisit how this works to generalize to all connections later.
663 /// Try not to use this function for other purposes.
664 ///
665 /// Normally the [`ServiceFs`] stream will block until all connections are closed.
666 /// In order to escrow the outgoing directory server endpoint, you may use this
667 /// function to get a [`StallableServiceFs`] that detects when no new requests
668 /// hit the outgoing directory for `debounce_interval`, and all hosted protocols
669 /// and other VFS connections to finish, then yield back the outgoing directory handle.
670 ///
671 /// The [`ServiceFs`] stream yields [`ServiceObjTy::Output`], which could be an enum
672 /// of FIDL connection requests in a typical component. By contrast, [`StallableServiceFs`]
673 /// yields an enum of either the request, or the unbound outgoing directory endpoint,
674 /// allowing you to escrow it back to `component_manager` before exiting the component.
675 pub fn until_stalled(
676 self,
677 debounce_interval: MonotonicDuration,
678 ) -> StallableServiceFs<ServiceObjTy> {
679 StallableServiceFs::<ServiceObjTy>::new(self, debounce_interval)
680 }
681}
682
683impl<ServiceObjTy: ServiceObjTrait> Stream for ServiceFs<ServiceObjTy> {
684 type Item = ServiceObjTy::Output;
685
686 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
687 // NOTE: Normally, it isn't safe to poll a stream after it returns None, but we support this
688 // and StallabkeServiceFs depends on this.
689 if let Some(channels) = self.channel_queue.take() {
690 for chan in channels {
691 self.serve_connection_impl(chan);
692 }
693 }
694 while let Poll::Ready(Some((index, channel))) =
695 self.new_connection_receiver.poll_next_unpin(cx)
696 {
697 if let Some(stream) = self.services[index].service().connect(channel) {
698 return Poll::Ready(Some(stream));
699 }
700 }
701 self.shutdown.poll_unpin(cx).map(|_| None)
702 }
703}