Skip to main content

runner/
component.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
5use async_trait::async_trait;
6use cm_types::NamespacePath;
7use fidl::endpoints::ServerEnd;
8use fidl::epitaph::ChannelEpitaphExt;
9use fidl::prelude::*;
10use fidl_fuchsia_component as fcomp;
11use fidl_fuchsia_component_runner as fcrunner;
12use fidl_fuchsia_io as fio;
13use fidl_fuchsia_process as fproc;
14use fuchsia_async as fasync;
15use fuchsia_runtime::{HandleInfo, HandleType, job_default};
16use futures::future::{BoxFuture, Either};
17use futures::prelude::*;
18#[cfg(fuchsia_api_level_at_least = "HEAD")]
19use futures::stream::BoxStream;
20use log::*;
21use namespace::Namespace;
22use std::sync::LazyLock;
23use thiserror::Error;
24use zx::{self as zx, HandleBased, Status};
25
26pub static PKG_PATH: LazyLock<NamespacePath> = LazyLock::new(|| "/pkg".parse().unwrap());
27
28/// Object implementing this type can be killed by calling kill function.
29#[async_trait]
30pub trait Controllable {
31    /// Should kill self and do cleanup.
32    /// Should not return error or panic, should log error instead.
33    async fn kill(&mut self);
34
35    /// Stop the component. Once the component is stopped, the
36    /// ComponentControllerControlHandle should be closed. If the component is
37    /// not stopped quickly enough, kill will be called. The amount of time
38    /// `stop` is allowed may vary based on a variety of factors.
39    fn stop<'a>(&mut self) -> BoxFuture<'a, ()>;
40
41    /// Perform any teardown tasks before closing the controller channel.
42    fn teardown<'a>(&mut self) -> BoxFuture<'a, ()> {
43        async {}.boxed()
44    }
45
46    /// Monitor any escrow requests from the component.
47    #[cfg(fuchsia_api_level_at_least = "HEAD")]
48    fn on_escrow<'a>(&self) -> BoxStream<'a, fcrunner::ComponentControllerOnEscrowRequest> {
49        futures::stream::empty().boxed()
50    }
51}
52
53/// Holds information about the component that allows the controller to
54/// interact with and control the component.
55pub struct Controller<C: Controllable> {
56    /// stream via which the component manager will ask the controller to
57    /// manipulate the component
58    request_stream: fcrunner::ComponentControllerRequestStream,
59
60    #[allow(dead_code)] // Only needed at HEAD
61    control: fcrunner::ComponentControllerControlHandle,
62
63    /// Controllable object which controls the underlying component.
64    /// This would be None once the object is killed.
65    controllable: Option<C>,
66
67    /// Task that forwards the `on_escrow` event.
68    #[cfg(fuchsia_api_level_at_least = "HEAD")]
69    on_escrow_monitor: fasync::Task<()>,
70}
71
72/// Information about a component's termination (fuchsia.component.runner/ComponentStopInfo)
73#[derive(Debug, Clone, PartialEq)]
74pub struct StopInfo {
75    pub termination_status: zx::Status,
76    pub exit_code: Option<i64>,
77}
78
79impl StopInfo {
80    pub fn from_status(s: zx::Status, c: Option<i64>) -> Self {
81        Self { termination_status: s, exit_code: c }
82    }
83
84    pub fn from_u32(s: u32, c: Option<i64>) -> Self {
85        Self {
86            termination_status: Status::from_raw(i32::try_from(s).unwrap_or(i32::MAX)),
87            exit_code: c,
88        }
89    }
90
91    pub fn from_error(s: fcomp::Error, c: Option<i64>) -> Self {
92        Self::from_u32(s.into_primitive().into(), c)
93    }
94
95    pub fn from_ok(c: Option<i64>) -> Self {
96        Self { termination_status: Status::OK, exit_code: c }
97    }
98}
99
100#[cfg(fuchsia_api_level_at_least = "HEAD")]
101impl From<StopInfo> for fcrunner::ComponentStopInfo {
102    fn from(info: StopInfo) -> Self {
103        Self {
104            termination_status: Some(info.termination_status.into_raw()),
105            exit_code: info.exit_code,
106            ..Default::default()
107        }
108    }
109}
110
111#[cfg(fuchsia_api_level_at_least = "HEAD")]
112impl From<fcrunner::ComponentStopInfo> for StopInfo {
113    fn from(value: fcrunner::ComponentStopInfo) -> Self {
114        Self {
115            termination_status: zx::Status::from_raw(value.termination_status.unwrap_or(0)),
116            exit_code: value.exit_code,
117        }
118    }
119}
120
121impl<C: Controllable + 'static> Controller<C> {
122    /// Creates new instance
123    pub fn new(
124        controllable: C,
125        requests: fcrunner::ComponentControllerRequestStream,
126        control: fcrunner::ComponentControllerControlHandle,
127    ) -> Controller<C> {
128        #[cfg(fuchsia_api_level_at_least = "HEAD")]
129        {
130            let on_escrow = controllable.on_escrow();
131            let on_escrow_monitor =
132                fasync::Task::spawn(Self::monitor_events(on_escrow, requests.control_handle()));
133            Controller {
134                controllable: Some(controllable),
135                request_stream: requests,
136                control,
137                on_escrow_monitor,
138            }
139        }
140        #[cfg(fuchsia_api_level_less_than = "HEAD")]
141        Controller { controllable: Some(controllable), request_stream: requests, control }
142    }
143
144    async fn serve_controller(&mut self) -> Result<(), ()> {
145        while let Ok(Some(request)) = self.request_stream.try_next().await {
146            match request {
147                fcrunner::ComponentControllerRequest::Stop { control_handle: _c } => {
148                    // Since stop takes some period of time to complete, call
149                    // it in a separate context so we can respond to other
150                    // requests.
151                    let stop_func = self.stop();
152                    fasync::Task::spawn(stop_func).detach();
153                }
154                fcrunner::ComponentControllerRequest::Kill { control_handle: _c } => {
155                    self.kill().await;
156                    return Ok(());
157                }
158                fcrunner::ComponentControllerRequest::_UnknownMethod { .. } => (),
159            }
160        }
161        // The channel closed
162        Err(())
163    }
164
165    #[cfg(fuchsia_api_level_at_least = "HEAD")]
166    async fn monitor_events(
167        mut on_escrow: impl Stream<Item = fcrunner::ComponentControllerOnEscrowRequest> + Unpin + Send,
168        control_handle: fcrunner::ComponentControllerControlHandle,
169    ) {
170        while let Some(event) = on_escrow.next().await {
171            control_handle
172                .send_on_escrow(event)
173                .unwrap_or_else(|err| error!(err:%; "failed to send OnEscrow event"));
174        }
175    }
176
177    /// Serve the request stream held by this Controller. `exit_fut` should
178    /// complete if the component exits and should return a value which is
179    /// either 0 (ZX_OK) or one of the fuchsia.component/Error constants
180    /// defined as valid in the fuchsia.component.runner/ComponentController
181    /// documentation. This function will return after `exit_fut` completes
182    /// or the request stream closes. In either case the request stream is
183    /// closed once this function returns since the stream itself, which owns
184    /// the channel, is dropped.
185    pub async fn serve(mut self, exit_fut: impl Future<Output = StopInfo> + Unpin) {
186        let stop_info = {
187            // Pin the server_controller future so we can use it with select
188            let request_server = self.serve_controller();
189            futures::pin_mut!(request_server);
190
191            // Get the result of waiting for `exit_fut` to complete while also
192            // polling the request server.
193            match future::select(exit_fut, request_server).await {
194                Either::Left((return_code, _controller_server)) => return_code,
195                Either::Right((serve_result, pending_close)) => match serve_result {
196                    Ok(()) => pending_close.await,
197                    Err(_) => {
198                        // Return directly because the controller channel
199                        // closed so there's no point in an epitaph.
200                        return;
201                    }
202                },
203            }
204        };
205
206        // Before closing the controller channel, perform teardown tasks if the runner configured
207        // them. This will only run if the component was not killed (otherwise `controllable` is
208        // `None`).
209        if let Some(mut controllable) = self.controllable.take() {
210            controllable.teardown().await;
211        }
212
213        // Drain any escrow events.
214        // TODO(https://fxbug.dev/326626515): Drain the escrow requests until no long readable
215        // instead of waiting for an unbounded amount of time if `on_escrow` never completes.
216        #[cfg(fuchsia_api_level_at_least = "HEAD")]
217        {
218            self.on_escrow_monitor.await;
219            _ = self.control.send_on_stop(stop_info.clone().into());
220        }
221        let _ = stop_info; // avoid unused error at stable API level
222        self.request_stream.control_handle().shutdown();
223    }
224
225    /// Kill the job and shutdown control handle supplied to this function.
226    async fn kill(&mut self) {
227        if let Some(mut controllable) = self.controllable.take() {
228            controllable.kill().await;
229        }
230    }
231
232    /// If we have a Controllable, ask it to stop the component.
233    fn stop<'a>(&mut self) -> BoxFuture<'a, ()> {
234        if self.controllable.is_some() {
235            self.controllable.as_mut().unwrap().stop()
236        } else {
237            async {}.boxed()
238        }
239    }
240}
241
242/// An error encountered trying to launch a component.
243#[derive(Clone, Debug, PartialEq, Eq, Error)]
244pub enum LaunchError {
245    #[error("invalid binary path {}", _0)]
246    InvalidBinaryPath(String),
247
248    #[error("/pkg missing in the namespace")]
249    MissingPkg,
250
251    #[error("error loading executable: {:?}", _0)]
252    LoadingExecutable(String),
253
254    #[error("cannot convert proxy to channel")]
255    DirectoryToChannel,
256
257    #[error("cannot create channels: {}", _0)]
258    ChannelCreation(zx_status::Status),
259
260    #[error("error loading 'lib' in /pkg: {:?}", _0)]
261    LibLoadError(String),
262
263    #[error("cannot create job: {}", _0)]
264    JobCreation(zx_status::Status),
265
266    #[error("cannot duplicate job: {}", _0)]
267    DuplicateJob(zx_status::Status),
268
269    #[error("cannot add args to launcher: {:?}", _0)]
270    AddArgs(String),
271
272    #[error("cannot add args to launcher: {:?}", _0)]
273    AddHandles(String),
274
275    #[error("cannot add args to launcher: {:?}", _0)]
276    AddNames(String),
277
278    #[error("cannot add env to launcher: {:?}", _0)]
279    AddEnvirons(String),
280
281    #[error("cannot set options for launcher: {:?}", _0)]
282    SetOptions(String),
283}
284
285/// Arguments to `configure_launcher` function.
286pub struct LauncherConfigArgs<'a> {
287    /// relative binary path to /pkg in `ns`.
288    pub bin_path: &'a str,
289
290    /// Name of the binary to add to `LaunchInfo`. This will be truncated to
291    /// `zx::sys::ZX_MAX_NAME_LEN` bytes.
292    pub name: &'a str,
293
294    /// The options used to create the process.
295    pub options: zx::ProcessOptions,
296
297    /// Arguments to binary. Binary path will be automatically
298    /// prepended so that should not be passed as first argument.
299    pub args: Option<Vec<String>>,
300
301    /// Namespace for binary process to be launched.
302    pub ns: Namespace,
303
304    /// Job in which process is launched. If None, a child job would be created in default one.
305    pub job: Option<zx::Job>,
306
307    /// Extra handle infos to add. This function all ready adds handles for default job and svc
308    /// loader.
309    pub handle_infos: Option<Vec<fproc::HandleInfo>>,
310
311    /// Extra names to add to namespace. by default only names from `ns` are added.
312    pub name_infos: Option<Vec<fproc::NameInfo>>,
313
314    /// Process environment to add to launcher.
315    pub environs: Option<Vec<String>>,
316
317    /// proxy for `fuchsia.proc.Launcher`.
318    pub launcher: &'a fproc::LauncherProxy,
319
320    /// Custom loader proxy. If None, /pkg/lib would be used to load libraries.
321    pub loader_proxy_chan: Option<zx::Channel>,
322
323    /// VMO containing mapping to executable binary. If None, it would be loaded from /pkg.
324    pub executable_vmo: Option<zx::Vmo>,
325}
326
327/// Configures launcher to launch process using passed params and creates launch info.
328/// This starts a library loader service, that will live as long as the handle for it given to the
329/// launcher is alive.
330pub async fn configure_launcher(
331    config_args: LauncherConfigArgs<'_>,
332) -> Result<fproc::LaunchInfo, LaunchError> {
333    // Locate the '/pkg' directory proxy previously added to the new component's namespace.
334    let pkg_dir = config_args.ns.get(&PKG_PATH).ok_or(LaunchError::MissingPkg)?;
335
336    // library_loader provides a helper function that we use to load the main executable from the
337    // package directory as a VMO in the same way that dynamic libraries are loaded. Doing this
338    // first allows launching to fail quickly and clearly in case the main executable can't be
339    // loaded with ZX_RIGHT_EXECUTE from the package directory.
340    let executable_vmo = match config_args.executable_vmo {
341        Some(v) => v,
342        None => library_loader::load_vmo(pkg_dir, &config_args.bin_path)
343            .await
344            .map_err(|e| LaunchError::LoadingExecutable(e.to_string()))?,
345    };
346
347    let ll_client_chan = match config_args.loader_proxy_chan {
348        None => {
349            // The loader service should only be able to load files from `/pkg/lib`. Giving it a
350            // larger scope is potentially a security vulnerability, as it could make it trivial for
351            // parts of applications to get handles to things the application author didn't intend.
352            let lib_proxy = fuchsia_component::directory::open_directory_async(
353                pkg_dir,
354                "lib",
355                fio::RX_STAR_DIR,
356            )
357            .map_err(|e| LaunchError::LibLoadError(e.to_string()))?;
358            let (ll_client_chan, ll_service_chan) = zx::Channel::create();
359            library_loader::start(lib_proxy.into(), ll_service_chan);
360            ll_client_chan
361        }
362        Some(chan) => chan,
363    };
364
365    // Get the provided job to create the new process in, if one was provided, or else create a new
366    // child job of this process's (this process that this code is running in) own 'default job'.
367    let job = config_args
368        .job
369        .unwrap_or(job_default().create_child_job().map_err(LaunchError::JobCreation)?);
370
371    // Build the command line args for the new process and send them to the launcher.
372    let bin_arg = PKG_PATH
373        .to_path_buf()
374        .join(&config_args.bin_path)
375        .to_str()
376        .ok_or_else(|| LaunchError::InvalidBinaryPath(config_args.bin_path.to_string()))?
377        .as_bytes()
378        .to_vec();
379    let mut all_args = vec![bin_arg];
380    if let Some(args) = config_args.args {
381        all_args.extend(args.into_iter().map(|s| s.into_bytes()));
382    }
383    config_args.launcher.add_args(&all_args).map_err(|e| LaunchError::AddArgs(e.to_string()))?;
384
385    // Get any initial handles to provide to the new process, if any were provided by the caller.
386    // Add handles for the new process's default job (by convention, this is the same job that the
387    // new process is launched in) and the fuchsia.ldsvc.Loader service created above, then send to
388    // the launcher.
389    let job_dup =
390        job.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(LaunchError::DuplicateJob)?;
391    let mut handle_infos = config_args.handle_infos.unwrap_or(vec![]);
392    handle_infos.append(&mut vec![
393        fproc::HandleInfo {
394            handle: ll_client_chan.into_handle(),
395            id: HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
396        },
397        fproc::HandleInfo {
398            handle: job_dup.into_handle(),
399            id: HandleInfo::new(HandleType::DefaultJob, 0).as_raw(),
400        },
401    ]);
402    config_args
403        .launcher
404        .add_handles(handle_infos)
405        .map_err(|e| LaunchError::AddHandles(e.to_string()))?;
406
407    if !config_args.options.is_empty() {
408        config_args
409            .launcher
410            .set_options(config_args.options.bits())
411            .map_err(|e| LaunchError::SetOptions(e.to_string()))?;
412    }
413
414    // Send environment variables for the new process, if any, to the launcher.
415    let environs: Vec<_> = config_args.environs.unwrap_or(vec![]);
416    if environs.len() > 0 {
417        let environs_bytes: Vec<_> = environs.into_iter().map(|s| s.into_bytes()).collect();
418        config_args
419            .launcher
420            .add_environs(&environs_bytes)
421            .map_err(|e| LaunchError::AddEnvirons(e.to_string()))?;
422    }
423
424    // Combine any manually provided namespace entries with the provided Namespace, and
425    // then send the new process's namespace to the launcher.
426    let mut name_infos = config_args.name_infos.unwrap_or(vec![]);
427    let ns: Vec<_> = config_args.ns.into();
428    name_infos.extend(ns.into_iter());
429    config_args.launcher.add_names(name_infos).map_err(|e| LaunchError::AddNames(e.to_string()))?;
430
431    let name = truncate_str(config_args.name, zx::sys::ZX_MAX_NAME_LEN).to_owned();
432
433    Ok(fproc::LaunchInfo { executable: executable_vmo, job, name })
434}
435
436/// Truncates `s` to be at most `max_len` bytes.
437fn truncate_str(s: &str, max_len: usize) -> &str {
438    let index = s.floor_char_boundary(max_len);
439    &s[..index]
440}
441
442static CONNECT_ERROR_HELP: &'static str = "To learn more, see \
443https://fuchsia.dev/go/components/connect-errors";
444
445/// Sets an epitaph on `ComponentController` `server_end` for a runner failure and the outgoing
446/// directory, and logs it.
447pub fn report_start_error(
448    err: zx::Status,
449    err_str: String,
450    resolved_url: &str,
451    controller_server_end: ServerEnd<fcrunner::ComponentControllerMarker>,
452) {
453    let _ = controller_server_end.into_channel().close_with_epitaph(err);
454    warn!("Failed to start component `{}`: {}\n{}", resolved_url, err_str, CONNECT_ERROR_HELP);
455}
456
457#[cfg(test)]
458mod tests {
459    use super::*;
460    use anyhow::Error;
461    use assert_matches::assert_matches;
462    use async_trait::async_trait;
463    use fidl::endpoints::{ClientEnd, create_endpoints, create_proxy};
464    use fidl_fuchsia_component_runner::{self as fcrunner, ComponentControllerProxy};
465    use fidl_fuchsia_io as fio;
466    use fidl_fuchsia_process as fproc;
467    use fuchsia_async as fasync;
468    use fuchsia_runtime::{HandleInfo, HandleType};
469    use futures::future::BoxFuture;
470    use futures::poll;
471    use namespace::{Namespace, NamespaceError};
472    use std::pin::Pin;
473    use std::task::Poll;
474    use zx::{self as zx, HandleBased};
475
476    #[test]
477    fn test_truncate_str() {
478        assert_eq!(truncate_str("", 0), "");
479        assert_eq!(truncate_str("", 1), "");
480
481        assert_eq!(truncate_str("été", 0), "");
482        assert_eq!(truncate_str("été", 1), "");
483        assert_eq!(truncate_str("été", 2), "é");
484        assert_eq!(truncate_str("été", 3), "ét");
485        assert_eq!(truncate_str("été", 4), "ét");
486        assert_eq!(truncate_str("été", 5), "été");
487        assert_eq!(truncate_str("été", 6), "été");
488    }
489
490    struct FakeComponent<K, J>
491    where
492        K: FnOnce() + std::marker::Send,
493        J: FnOnce() + std::marker::Send,
494    {
495        pub onkill: Option<K>,
496
497        pub onstop: Option<J>,
498
499        pub onteardown: Option<BoxFuture<'static, ()>>,
500    }
501
502    #[async_trait]
503    impl<K: 'static, J: 'static> Controllable for FakeComponent<K, J>
504    where
505        K: FnOnce() + std::marker::Send,
506        J: FnOnce() + std::marker::Send,
507    {
508        async fn kill(&mut self) {
509            let func = self.onkill.take().unwrap();
510            func();
511        }
512
513        fn stop<'a>(&mut self) -> BoxFuture<'a, ()> {
514            let func = self.onstop.take().unwrap();
515            async move { func() }.boxed()
516        }
517
518        fn teardown<'a>(&mut self) -> BoxFuture<'a, ()> {
519            self.onteardown.take().unwrap()
520        }
521    }
522
523    #[fuchsia::test]
524    async fn test_kill_component() -> Result<(), Error> {
525        let (sender, recv) = futures::channel::oneshot::channel::<()>();
526        let (term_tx, term_rx) = futures::channel::oneshot::channel::<StopInfo>();
527        let stop_info = StopInfo::from_ok(Some(42));
528        let fake_component = FakeComponent {
529            onkill: Some(move || {
530                sender.send(()).unwrap();
531                // After acknowledging that we received kill, send the status
532                // value so `serve` completes.
533                let _ = term_tx.send(stop_info.clone());
534            }),
535            onstop: Some(|| {}),
536            onteardown: Some(async {}.boxed()),
537        };
538
539        let (controller, client_proxy) = create_controller_and_proxy(fake_component)?;
540
541        client_proxy.kill().expect("FIDL error returned from kill request to controller");
542
543        let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
544        // this should return after kill call
545        controller.serve(term_receiver).await;
546
547        // this means kill was called
548        recv.await?;
549
550        // Check the event on the controller channel, this should match what
551        // is sent by `term_tx`
552        let mut event_stream = client_proxy.take_event_stream();
553        assert_matches!(
554            event_stream.try_next().await,
555            Ok(Some(fcrunner::ComponentControllerEvent::OnStop {
556                payload: fcrunner::ComponentStopInfo {
557                    termination_status: Some(0),
558                    exit_code: Some(42),
559                    ..
560                }
561            }))
562        );
563        assert_matches!(event_stream.try_next().await, Ok(None));
564
565        Ok(())
566    }
567
568    #[fuchsia::test]
569    async fn test_stop_component() -> Result<(), Error> {
570        let (sender, recv) = futures::channel::oneshot::channel::<()>();
571        let (teardown_signal_tx, teardown_signal_rx) = futures::channel::oneshot::channel::<()>();
572        let (teardown_fence_tx, teardown_fence_rx) = futures::channel::oneshot::channel::<()>();
573        let (term_tx, term_rx) = futures::channel::oneshot::channel::<StopInfo>();
574        let stop_info = StopInfo::from_ok(Some(42));
575
576        let fake_component = FakeComponent {
577            onstop: Some(move || {
578                sender.send(()).unwrap();
579                let _ = term_tx.send(stop_info.clone());
580            }),
581            onkill: Some(move || {}),
582            onteardown: Some(
583                async move {
584                    teardown_signal_tx.send(()).unwrap();
585                    teardown_fence_rx.await.unwrap();
586                }
587                .boxed(),
588            ),
589        };
590
591        let (controller, client_proxy) = create_controller_and_proxy(fake_component)?;
592
593        client_proxy.stop().expect("FIDL error returned from kill request to controller");
594
595        let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
596
597        // This should return once the channel is closed, that is after stop and teardown
598        let controller_serve = fasync::Task::spawn(controller.serve(term_receiver));
599
600        // This means stop was called
601        recv.await?;
602
603        // Teardown should be called
604        teardown_signal_rx.await?;
605
606        // Teardown is blocked. Verify there's no event on the channel yet, then unblock it.
607        let mut client_stream = client_proxy.take_event_stream();
608        let mut client_stream_fut = client_stream.try_next();
609        assert_matches!(poll!(Pin::new(&mut client_stream_fut)), Poll::Pending);
610        teardown_fence_tx.send(()).unwrap();
611        controller_serve.await;
612
613        // Check the event on the controller channel, this should match what
614        // is sent by `term_tx`
615        assert_matches!(
616            client_stream_fut.await,
617            Ok(Some(fcrunner::ComponentControllerEvent::OnStop {
618                payload: fcrunner::ComponentStopInfo {
619                    termination_status: Some(0),
620                    exit_code: Some(42),
621                    ..
622                }
623            }))
624        );
625        assert_matches!(client_stream.try_next().await, Ok(None));
626
627        Ok(())
628    }
629
630    #[fuchsia::test]
631    fn test_stop_then_kill() -> Result<(), Error> {
632        let mut exec = fasync::TestExecutor::new();
633        let (sender, mut recv) = futures::channel::oneshot::channel::<()>();
634        let (term_tx, term_rx) = futures::channel::oneshot::channel::<StopInfo>();
635        let stop_info = StopInfo::from_ok(Some(42));
636
637        // This component will only 'exit' after kill is called.
638        let fake_component = FakeComponent {
639            onstop: Some(move || {
640                sender.send(()).unwrap();
641            }),
642            onkill: Some(move || {
643                let _ = term_tx.send(stop_info.clone());
644            }),
645            onteardown: Some(async {}.boxed()),
646        };
647
648        let (controller, client_proxy) = create_controller_and_proxy(fake_component)?;
649        // Send a stop request, note that the controller isn't even running
650        // yet, but the request will be waiting in the channel when it does.
651        client_proxy.stop().expect("FIDL error returned from stop request to controller");
652
653        // Set up the controller to run.
654        let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
655        let mut controller_fut = Box::pin(controller.serve(term_receiver));
656
657        // Run the serve loop until it is stalled, it shouldn't return because
658        // stop doesn't automatically call exit.
659        match exec.run_until_stalled(&mut controller_fut) {
660            Poll::Pending => {}
661            x => panic!("Serve future should have been pending but was not {:?}", x),
662        }
663
664        // Check that stop was called
665        assert_eq!(exec.run_until_stalled(&mut recv), Poll::Ready(Ok(())));
666
667        // Kill the component which should call the `onkill` we passed in.
668        // This should cause the termination future to complete, which should then
669        // cause the controller future to complete.
670        client_proxy.kill().expect("FIDL error returned from kill request to controller");
671        match exec.run_until_stalled(&mut controller_fut) {
672            Poll::Ready(()) => {}
673            x => panic!("Unexpected controller poll state {:?}", x),
674        }
675
676        // Check the controller channel closed with an event that matches
677        // what was sent in the `onkill` closure.
678        let mut event_stream = client_proxy.take_event_stream();
679        let mut next_fut = event_stream.try_next();
680        assert_matches!(
681            exec.run_until_stalled(&mut next_fut),
682            Poll::Ready(Ok(Some(fcrunner::ComponentControllerEvent::OnStop {
683                payload: fcrunner::ComponentStopInfo {
684                    termination_status: Some(0),
685                    exit_code: Some(42),
686                    ..
687                }
688            })))
689        );
690
691        let mut next_fut = event_stream.try_next();
692        assert_matches!(exec.run_until_stalled(&mut next_fut), Poll::Ready(Ok(None)));
693        Ok(())
694    }
695
696    fn create_controller_and_proxy<K: 'static, J: 'static>(
697        fake_component: FakeComponent<K, J>,
698    ) -> Result<(Controller<FakeComponent<K, J>>, ComponentControllerProxy), Error>
699    where
700        K: FnOnce() + std::marker::Send,
701        J: FnOnce() + std::marker::Send,
702    {
703        let (client_endpoint, server_endpoint) =
704            create_endpoints::<fcrunner::ComponentControllerMarker>();
705
706        // Get a proxy to the ComponentController channel.
707        let (controller_stream, control) = server_endpoint.into_stream_and_control_handle();
708        Ok((
709            Controller::new(fake_component, controller_stream, control),
710            client_endpoint.into_proxy(),
711        ))
712    }
713
714    mod launch_info {
715        use fidl::endpoints::Proxy;
716
717        use super::*;
718        use anyhow::format_err;
719        use futures::channel::oneshot;
720
721        fn setup_empty_namespace() -> Result<Namespace, NamespaceError> {
722            setup_namespace(false, vec![])
723        }
724
725        fn setup_namespace(
726            include_pkg: bool,
727            // All the handles created for this will have server end closed.
728            // Clients cannot send messages on those handles in ns.
729            extra_paths: Vec<&str>,
730        ) -> Result<Namespace, NamespaceError> {
731            let mut ns = Vec::<fcrunner::ComponentNamespaceEntry>::new();
732            if include_pkg {
733                let pkg_path = "/pkg".to_string();
734                let pkg_chan = fuchsia_fs::directory::open_in_namespace(
735                    "/pkg",
736                    fio::PERM_READABLE | fio::PERM_EXECUTABLE,
737                )
738                .unwrap()
739                .into_channel()
740                .unwrap()
741                .into_zx_channel();
742                let pkg_handle = ClientEnd::new(pkg_chan);
743
744                ns.push(fcrunner::ComponentNamespaceEntry {
745                    path: Some(pkg_path),
746                    directory: Some(pkg_handle),
747                    ..Default::default()
748                });
749            }
750
751            for path in extra_paths {
752                let (client, _server) = create_endpoints::<fio::DirectoryMarker>();
753                ns.push(fcrunner::ComponentNamespaceEntry {
754                    path: Some(path.to_string()),
755                    directory: Some(client),
756                    ..Default::default()
757                });
758            }
759            Namespace::try_from(ns)
760        }
761
762        #[derive(Default)]
763        struct FakeLauncherServiceResults {
764            names: Vec<String>,
765            handles: Vec<u32>,
766            args: Vec<String>,
767            options: zx::ProcessOptions,
768        }
769
770        fn start_launcher()
771        -> Result<(fproc::LauncherProxy, oneshot::Receiver<FakeLauncherServiceResults>), Error>
772        {
773            let (launcher_proxy, server_end) = create_proxy::<fproc::LauncherMarker>();
774            let (sender, receiver) = oneshot::channel();
775            fasync::Task::local(async move {
776                let stream = server_end.into_stream();
777                run_launcher_service(stream, sender)
778                    .await
779                    .expect("error running fake launcher service");
780            })
781            .detach();
782            Ok((launcher_proxy, receiver))
783        }
784
785        async fn run_launcher_service(
786            mut stream: fproc::LauncherRequestStream,
787            sender: oneshot::Sender<FakeLauncherServiceResults>,
788        ) -> Result<(), Error> {
789            let mut res = FakeLauncherServiceResults::default();
790            while let Some(event) = stream.try_next().await? {
791                match event {
792                    fproc::LauncherRequest::AddArgs { args, .. } => {
793                        res.args.extend(
794                            args.into_iter()
795                                .map(|a| {
796                                    std::str::from_utf8(&a)
797                                        .expect("cannot convert bytes to utf8 string")
798                                        .to_owned()
799                                })
800                                .collect::<Vec<String>>(),
801                        );
802                    }
803                    fproc::LauncherRequest::AddEnvirons { .. } => {}
804                    fproc::LauncherRequest::AddNames { names, .. } => {
805                        res.names
806                            .extend(names.into_iter().map(|m| m.path).collect::<Vec<String>>());
807                    }
808                    fproc::LauncherRequest::AddHandles { handles, .. } => {
809                        res.handles.extend(handles.into_iter().map(|m| m.id).collect::<Vec<u32>>());
810                    }
811                    fproc::LauncherRequest::SetOptions { options, .. } => {
812                        res.options = zx::ProcessOptions::from_bits_retain(options);
813                    }
814                    fproc::LauncherRequest::CreateWithoutStarting { .. } => {}
815                    fproc::LauncherRequest::Launch { .. } => {}
816                }
817            }
818            sender.send(res).map_err(|_e| format_err!("can't send result"))?;
819            Ok(())
820        }
821
822        #[fuchsia::test]
823        async fn missing_pkg() -> Result<(), Error> {
824            let (launcher_proxy, _server_end) = create_proxy::<fproc::LauncherMarker>();
825            let ns = setup_empty_namespace()?;
826
827            assert_eq!(
828                configure_launcher(LauncherConfigArgs {
829                    bin_path: "bin/path",
830                    name: "name",
831                    args: None,
832                    options: zx::ProcessOptions::empty(),
833                    ns: ns,
834                    job: None,
835                    handle_infos: None,
836                    name_infos: None,
837                    environs: None,
838                    launcher: &launcher_proxy,
839                    loader_proxy_chan: None,
840                    executable_vmo: None
841                })
842                .await,
843                Err(LaunchError::MissingPkg),
844            );
845
846            drop(_server_end);
847            Ok(())
848        }
849
850        #[fuchsia::test]
851        async fn invalid_executable() -> Result<(), Error> {
852            let (launcher_proxy, _server_end) = create_proxy::<fproc::LauncherMarker>();
853            let ns = setup_namespace(true, vec![])?;
854
855            match configure_launcher(LauncherConfigArgs {
856                bin_path: "test/path",
857                name: "name",
858                args: None,
859                options: zx::ProcessOptions::empty(),
860                ns: ns,
861                job: None,
862                handle_infos: None,
863                name_infos: None,
864                environs: None,
865                launcher: &launcher_proxy,
866                loader_proxy_chan: None,
867                executable_vmo: None,
868            })
869            .await
870            .expect_err("should error out")
871            {
872                LaunchError::LoadingExecutable(_) => {}
873                e => panic!("Expected LoadingExecutable error, got {:?}", e),
874            }
875            Ok(())
876        }
877
878        #[fuchsia::test]
879        async fn invalid_pkg() -> Result<(), Error> {
880            let (launcher_proxy, _server_end) = create_proxy::<fproc::LauncherMarker>();
881            let ns = setup_namespace(false, vec!["/pkg"])?;
882
883            match configure_launcher(LauncherConfigArgs {
884                bin_path: "bin/path",
885                name: "name",
886                args: None,
887                options: zx::ProcessOptions::empty(),
888                ns: ns,
889                job: None,
890                handle_infos: None,
891                name_infos: None,
892                environs: None,
893                launcher: &launcher_proxy,
894                loader_proxy_chan: None,
895                executable_vmo: None,
896            })
897            .await
898            .expect_err("should error out")
899            {
900                LaunchError::LoadingExecutable(_) => {}
901                e => panic!("Expected LoadingExecutable error, got {:?}", e),
902            }
903            Ok(())
904        }
905
906        #[fuchsia::test]
907        async fn default_args() -> Result<(), Error> {
908            let (launcher_proxy, recv) = start_launcher()?;
909
910            let ns = setup_namespace(true, vec![])?;
911
912            let _launch_info = configure_launcher(LauncherConfigArgs {
913                bin_path: "bin/runner_lib_test",
914                name: "name",
915                args: None,
916                options: zx::ProcessOptions::empty(),
917                ns: ns,
918                job: None,
919                handle_infos: None,
920                name_infos: None,
921                environs: None,
922                launcher: &launcher_proxy,
923                loader_proxy_chan: None,
924                executable_vmo: None,
925            })
926            .await?;
927
928            drop(launcher_proxy);
929
930            let ls = recv.await?;
931
932            assert_eq!(ls.args, vec!("/pkg/bin/runner_lib_test".to_owned()));
933
934            Ok(())
935        }
936
937        #[fasync::run_singlethreaded(test)]
938        async fn custom_executable_vmo() -> Result<(), Error> {
939            let (launcher_proxy, _recv) = start_launcher()?;
940
941            let ns = setup_namespace(true, vec![])?;
942            let vmo = zx::Vmo::create(100)?;
943            vmo.write(b"my_data", 0)?;
944            let launch_info = configure_launcher(LauncherConfigArgs {
945                bin_path: "bin/runner_lib_test",
946                name: "name",
947                args: None,
948                options: zx::ProcessOptions::empty(),
949                ns: ns,
950                job: None,
951                handle_infos: None,
952                name_infos: None,
953                environs: None,
954                launcher: &launcher_proxy,
955                loader_proxy_chan: None,
956                executable_vmo: Some(vmo),
957            })
958            .await?;
959
960            let mut bytes: [u8; 10] = [0; 10];
961            launch_info.executable.read(&mut bytes, 0)?;
962            let expected = b"my_data";
963            assert_eq!(bytes[0..expected.len()], expected[..]);
964            Ok(())
965        }
966
967        #[fasync::run_singlethreaded(test)]
968        async fn extra_args() -> Result<(), Error> {
969            let (launcher_proxy, recv) = start_launcher()?;
970
971            let ns = setup_namespace(true, vec![])?;
972
973            let args = vec!["args1".to_owned(), "arg2".to_owned()];
974
975            let _launch_info = configure_launcher(LauncherConfigArgs {
976                bin_path: "bin/runner_lib_test",
977                name: "name",
978                args: Some(args.clone()),
979                options: zx::ProcessOptions::empty(),
980                ns: ns,
981                job: None,
982                handle_infos: None,
983                name_infos: None,
984                environs: None,
985                launcher: &launcher_proxy,
986                loader_proxy_chan: None,
987                executable_vmo: None,
988            })
989            .await?;
990
991            drop(launcher_proxy);
992
993            let ls = recv.await?;
994
995            let mut expected = vec!["/pkg/bin/runner_lib_test".to_owned()];
996            expected.extend(args);
997            assert_eq!(ls.args, expected);
998
999            Ok(())
1000        }
1001
1002        #[fasync::run_singlethreaded(test)]
1003        async fn namespace_added() -> Result<(), Error> {
1004            let (launcher_proxy, recv) = start_launcher()?;
1005
1006            let ns = setup_namespace(true, vec!["/some_path1", "/some_path2"])?;
1007
1008            let _launch_info = configure_launcher(LauncherConfigArgs {
1009                bin_path: "bin/runner_lib_test",
1010                name: "name",
1011                args: None,
1012                options: zx::ProcessOptions::empty(),
1013                ns: ns,
1014                job: None,
1015                handle_infos: None,
1016                name_infos: None,
1017                environs: None,
1018                launcher: &launcher_proxy,
1019                loader_proxy_chan: None,
1020                executable_vmo: None,
1021            })
1022            .await?;
1023
1024            drop(launcher_proxy);
1025
1026            let ls = recv.await?;
1027
1028            let mut names = ls.names;
1029            names.sort();
1030            assert_eq!(
1031                names,
1032                vec!("/pkg", "/some_path1", "/some_path2")
1033                    .into_iter()
1034                    .map(|s| s.to_string())
1035                    .collect::<Vec<String>>()
1036            );
1037
1038            Ok(())
1039        }
1040
1041        #[fasync::run_singlethreaded(test)]
1042        async fn extra_namespace_entries() -> Result<(), Error> {
1043            let (launcher_proxy, recv) = start_launcher()?;
1044
1045            let ns = setup_namespace(true, vec!["/some_path1", "/some_path2"])?;
1046
1047            let mut names = vec![];
1048
1049            let extra_paths = vec!["/extra1", "/extra2"];
1050
1051            for path in &extra_paths {
1052                let (client, _server) = create_endpoints::<fio::DirectoryMarker>();
1053
1054                names.push(fproc::NameInfo { path: path.to_string(), directory: client });
1055            }
1056
1057            let _launch_info = configure_launcher(LauncherConfigArgs {
1058                bin_path: "bin/runner_lib_test",
1059                name: "name",
1060                args: None,
1061                options: zx::ProcessOptions::empty(),
1062                ns: ns,
1063                job: None,
1064                handle_infos: None,
1065                name_infos: Some(names),
1066                environs: None,
1067                launcher: &launcher_proxy,
1068                loader_proxy_chan: None,
1069                executable_vmo: None,
1070            })
1071            .await?;
1072
1073            drop(launcher_proxy);
1074
1075            let ls = recv.await?;
1076
1077            let mut paths = vec!["/pkg", "/some_path1", "/some_path2"];
1078            paths.extend(extra_paths.into_iter());
1079            paths.sort();
1080
1081            let mut ls_names = ls.names;
1082            ls_names.sort();
1083
1084            assert_eq!(ls_names, paths.into_iter().map(|s| s.to_string()).collect::<Vec<String>>());
1085
1086            Ok(())
1087        }
1088
1089        #[fasync::run_singlethreaded(test)]
1090        async fn handles_added() -> Result<(), Error> {
1091            let (launcher_proxy, recv) = start_launcher()?;
1092
1093            let ns = setup_namespace(true, vec![])?;
1094
1095            let _launch_info = configure_launcher(LauncherConfigArgs {
1096                bin_path: "bin/runner_lib_test",
1097                name: "name",
1098                args: None,
1099                options: zx::ProcessOptions::empty(),
1100                ns: ns,
1101                job: None,
1102                handle_infos: None,
1103                name_infos: None,
1104                environs: None,
1105                launcher: &launcher_proxy,
1106                loader_proxy_chan: None,
1107                executable_vmo: None,
1108            })
1109            .await?;
1110
1111            drop(launcher_proxy);
1112
1113            let ls = recv.await?;
1114
1115            assert_eq!(
1116                ls.handles,
1117                vec!(
1118                    HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
1119                    HandleInfo::new(HandleType::DefaultJob, 0).as_raw()
1120                )
1121            );
1122
1123            Ok(())
1124        }
1125
1126        #[fasync::run_singlethreaded(test)]
1127        async fn handles_added_with_custom_loader_chan() -> Result<(), Error> {
1128            let (launcher_proxy, recv) = start_launcher()?;
1129
1130            let (c1, _c2) = zx::Channel::create();
1131
1132            let ns = setup_namespace(true, vec![])?;
1133
1134            let _launch_info = configure_launcher(LauncherConfigArgs {
1135                bin_path: "bin/runner_lib_test",
1136                name: "name",
1137                args: None,
1138                options: zx::ProcessOptions::empty(),
1139                ns: ns,
1140                job: None,
1141                handle_infos: None,
1142                name_infos: None,
1143                environs: None,
1144                launcher: &launcher_proxy,
1145                loader_proxy_chan: Some(c1),
1146                executable_vmo: None,
1147            })
1148            .await?;
1149
1150            drop(launcher_proxy);
1151
1152            let ls = recv.await?;
1153
1154            assert_eq!(
1155                ls.handles,
1156                vec!(
1157                    HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
1158                    HandleInfo::new(HandleType::DefaultJob, 0).as_raw()
1159                )
1160            );
1161
1162            Ok(())
1163        }
1164
1165        #[fasync::run_singlethreaded(test)]
1166        async fn extra_handles() -> Result<(), Error> {
1167            let (launcher_proxy, recv) = start_launcher()?;
1168
1169            let ns = setup_namespace(true, vec![])?;
1170
1171            let mut handle_infos = vec![];
1172            for fd in 0..3 {
1173                let (client, _server) = create_endpoints::<fio::DirectoryMarker>();
1174                handle_infos.push(fproc::HandleInfo {
1175                    handle: client.into_channel().into_handle(),
1176                    id: fd,
1177                });
1178            }
1179
1180            let _launch_info = configure_launcher(LauncherConfigArgs {
1181                bin_path: "bin/runner_lib_test",
1182                name: "name",
1183                args: None,
1184                options: zx::ProcessOptions::empty(),
1185                ns: ns,
1186                job: None,
1187                handle_infos: Some(handle_infos),
1188                name_infos: None,
1189                environs: None,
1190                launcher: &launcher_proxy,
1191                loader_proxy_chan: None,
1192                executable_vmo: None,
1193            })
1194            .await?;
1195
1196            drop(launcher_proxy);
1197
1198            let ls = recv.await?;
1199
1200            assert_eq!(
1201                ls.handles,
1202                vec!(
1203                    0,
1204                    1,
1205                    2,
1206                    HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
1207                    HandleInfo::new(HandleType::DefaultJob, 0).as_raw(),
1208                )
1209            );
1210
1211            Ok(())
1212        }
1213    }
1214}