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::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
475    #[test]
476    fn test_truncate_str() {
477        assert_eq!(truncate_str("", 0), "");
478        assert_eq!(truncate_str("", 1), "");
479
480        assert_eq!(truncate_str("été", 0), "");
481        assert_eq!(truncate_str("été", 1), "");
482        assert_eq!(truncate_str("été", 2), "é");
483        assert_eq!(truncate_str("été", 3), "ét");
484        assert_eq!(truncate_str("été", 4), "ét");
485        assert_eq!(truncate_str("été", 5), "été");
486        assert_eq!(truncate_str("été", 6), "été");
487    }
488
489    struct FakeComponent<K, J>
490    where
491        K: FnOnce() + std::marker::Send,
492        J: FnOnce() + std::marker::Send,
493    {
494        pub onkill: Option<K>,
495
496        pub onstop: Option<J>,
497
498        pub onteardown: Option<BoxFuture<'static, ()>>,
499    }
500
501    #[async_trait]
502    impl<K: 'static, J: 'static> Controllable for FakeComponent<K, J>
503    where
504        K: FnOnce() + std::marker::Send,
505        J: FnOnce() + std::marker::Send,
506    {
507        async fn kill(&mut self) {
508            let func = self.onkill.take().unwrap();
509            func();
510        }
511
512        fn stop<'a>(&mut self) -> BoxFuture<'a, ()> {
513            let func = self.onstop.take().unwrap();
514            async move { func() }.boxed()
515        }
516
517        fn teardown<'a>(&mut self) -> BoxFuture<'a, ()> {
518            self.onteardown.take().unwrap()
519        }
520    }
521
522    #[fuchsia::test]
523    async fn test_kill_component() -> Result<(), Error> {
524        let (sender, recv) = futures::channel::oneshot::channel::<()>();
525        let (term_tx, term_rx) = futures::channel::oneshot::channel::<StopInfo>();
526        let stop_info = StopInfo::from_ok(Some(42));
527        let fake_component = FakeComponent {
528            onkill: Some(move || {
529                sender.send(()).unwrap();
530                // After acknowledging that we received kill, send the status
531                // value so `serve` completes.
532                let _ = term_tx.send(stop_info.clone());
533            }),
534            onstop: Some(|| {}),
535            onteardown: Some(async {}.boxed()),
536        };
537
538        let (controller, client_proxy) = create_controller_and_proxy(fake_component)?;
539
540        client_proxy.kill().expect("FIDL error returned from kill request to controller");
541
542        let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
543        // this should return after kill call
544        controller.serve(term_receiver).await;
545
546        // this means kill was called
547        recv.await?;
548
549        // Check the event on the controller channel, this should match what
550        // is sent by `term_tx`
551        let mut event_stream = client_proxy.take_event_stream();
552        assert_matches!(
553            event_stream.try_next().await,
554            Ok(Some(fcrunner::ComponentControllerEvent::OnStop {
555                payload: fcrunner::ComponentStopInfo {
556                    termination_status: Some(0),
557                    exit_code: Some(42),
558                    ..
559                }
560            }))
561        );
562        assert_matches!(event_stream.try_next().await, Ok(None));
563
564        Ok(())
565    }
566
567    #[fuchsia::test]
568    async fn test_stop_component() -> Result<(), Error> {
569        let (sender, recv) = futures::channel::oneshot::channel::<()>();
570        let (teardown_signal_tx, teardown_signal_rx) = futures::channel::oneshot::channel::<()>();
571        let (teardown_fence_tx, teardown_fence_rx) = futures::channel::oneshot::channel::<()>();
572        let (term_tx, term_rx) = futures::channel::oneshot::channel::<StopInfo>();
573        let stop_info = StopInfo::from_ok(Some(42));
574
575        let fake_component = FakeComponent {
576            onstop: Some(move || {
577                sender.send(()).unwrap();
578                let _ = term_tx.send(stop_info.clone());
579            }),
580            onkill: Some(move || {}),
581            onteardown: Some(
582                async move {
583                    teardown_signal_tx.send(()).unwrap();
584                    teardown_fence_rx.await.unwrap();
585                }
586                .boxed(),
587            ),
588        };
589
590        let (controller, client_proxy) = create_controller_and_proxy(fake_component)?;
591
592        client_proxy.stop().expect("FIDL error returned from kill request to controller");
593
594        let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
595
596        // This should return once the channel is closed, that is after stop and teardown
597        let controller_serve = fasync::Task::spawn(controller.serve(term_receiver));
598
599        // This means stop was called
600        recv.await?;
601
602        // Teardown should be called
603        teardown_signal_rx.await?;
604
605        // Teardown is blocked. Verify there's no event on the channel yet, then unblock it.
606        let mut client_stream = client_proxy.take_event_stream();
607        let mut client_stream_fut = client_stream.try_next();
608        assert_matches!(poll!(Pin::new(&mut client_stream_fut)), Poll::Pending);
609        teardown_fence_tx.send(()).unwrap();
610        controller_serve.await;
611
612        // Check the event on the controller channel, this should match what
613        // is sent by `term_tx`
614        assert_matches!(
615            client_stream_fut.await,
616            Ok(Some(fcrunner::ComponentControllerEvent::OnStop {
617                payload: fcrunner::ComponentStopInfo {
618                    termination_status: Some(0),
619                    exit_code: Some(42),
620                    ..
621                }
622            }))
623        );
624        assert_matches!(client_stream.try_next().await, Ok(None));
625
626        Ok(())
627    }
628
629    #[fuchsia::test]
630    fn test_stop_then_kill() -> Result<(), Error> {
631        let mut exec = fasync::TestExecutor::new();
632        let (sender, mut recv) = futures::channel::oneshot::channel::<()>();
633        let (term_tx, term_rx) = futures::channel::oneshot::channel::<StopInfo>();
634        let stop_info = StopInfo::from_ok(Some(42));
635
636        // This component will only 'exit' after kill is called.
637        let fake_component = FakeComponent {
638            onstop: Some(move || {
639                sender.send(()).unwrap();
640            }),
641            onkill: Some(move || {
642                let _ = term_tx.send(stop_info.clone());
643            }),
644            onteardown: Some(async {}.boxed()),
645        };
646
647        let (controller, client_proxy) = create_controller_and_proxy(fake_component)?;
648        // Send a stop request, note that the controller isn't even running
649        // yet, but the request will be waiting in the channel when it does.
650        client_proxy.stop().expect("FIDL error returned from stop request to controller");
651
652        // Set up the controller to run.
653        let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
654        let mut controller_fut = Box::pin(controller.serve(term_receiver));
655
656        // Run the serve loop until it is stalled, it shouldn't return because
657        // stop doesn't automatically call exit.
658        match exec.run_until_stalled(&mut controller_fut) {
659            Poll::Pending => {}
660            x => panic!("Serve future should have been pending but was not {:?}", x),
661        }
662
663        // Check that stop was called
664        assert_eq!(exec.run_until_stalled(&mut recv), Poll::Ready(Ok(())));
665
666        // Kill the component which should call the `onkill` we passed in.
667        // This should cause the termination future to complete, which should then
668        // cause the controller future to complete.
669        client_proxy.kill().expect("FIDL error returned from kill request to controller");
670        match exec.run_until_stalled(&mut controller_fut) {
671            Poll::Ready(()) => {}
672            x => panic!("Unexpected controller poll state {:?}", x),
673        }
674
675        // Check the controller channel closed with an event that matches
676        // what was sent in the `onkill` closure.
677        let mut event_stream = client_proxy.take_event_stream();
678        let mut next_fut = event_stream.try_next();
679        assert_matches!(
680            exec.run_until_stalled(&mut next_fut),
681            Poll::Ready(Ok(Some(fcrunner::ComponentControllerEvent::OnStop {
682                payload: fcrunner::ComponentStopInfo {
683                    termination_status: Some(0),
684                    exit_code: Some(42),
685                    ..
686                }
687            })))
688        );
689
690        let mut next_fut = event_stream.try_next();
691        assert_matches!(exec.run_until_stalled(&mut next_fut), Poll::Ready(Ok(None)));
692        Ok(())
693    }
694
695    fn create_controller_and_proxy<K: 'static, J: 'static>(
696        fake_component: FakeComponent<K, J>,
697    ) -> Result<(Controller<FakeComponent<K, J>>, ComponentControllerProxy), Error>
698    where
699        K: FnOnce() + std::marker::Send,
700        J: FnOnce() + std::marker::Send,
701    {
702        let (client_endpoint, server_endpoint) =
703            create_endpoints::<fcrunner::ComponentControllerMarker>();
704
705        // Get a proxy to the ComponentController channel.
706        let (controller_stream, control) = server_endpoint.into_stream_and_control_handle();
707        Ok((
708            Controller::new(fake_component, controller_stream, control),
709            client_endpoint.into_proxy(),
710        ))
711    }
712
713    mod launch_info {
714        use fidl::endpoints::Proxy;
715
716        use super::*;
717        use anyhow::format_err;
718        use futures::channel::oneshot;
719
720        fn setup_empty_namespace() -> Result<Namespace, NamespaceError> {
721            setup_namespace(false, vec![])
722        }
723
724        fn setup_namespace(
725            include_pkg: bool,
726            // All the handles created for this will have server end closed.
727            // Clients cannot send messages on those handles in ns.
728            extra_paths: Vec<&str>,
729        ) -> Result<Namespace, NamespaceError> {
730            let mut ns = Vec::<fcrunner::ComponentNamespaceEntry>::new();
731            if include_pkg {
732                let pkg_path = "/pkg".to_string();
733                let pkg_chan = fuchsia_fs::directory::open_in_namespace(
734                    "/pkg",
735                    fio::PERM_READABLE | fio::PERM_EXECUTABLE,
736                )
737                .unwrap()
738                .into_channel()
739                .unwrap()
740                .into_zx_channel();
741                let pkg_handle = ClientEnd::new(pkg_chan);
742
743                ns.push(fcrunner::ComponentNamespaceEntry {
744                    path: Some(pkg_path),
745                    directory: Some(pkg_handle),
746                    ..Default::default()
747                });
748            }
749
750            for path in extra_paths {
751                let (client, _server) = create_endpoints::<fio::DirectoryMarker>();
752                ns.push(fcrunner::ComponentNamespaceEntry {
753                    path: Some(path.to_string()),
754                    directory: Some(client),
755                    ..Default::default()
756                });
757            }
758            Namespace::try_from(ns)
759        }
760
761        #[derive(Default)]
762        struct FakeLauncherServiceResults {
763            names: Vec<String>,
764            handles: Vec<u32>,
765            args: Vec<String>,
766            options: zx::ProcessOptions,
767        }
768
769        fn start_launcher()
770        -> Result<(fproc::LauncherProxy, oneshot::Receiver<FakeLauncherServiceResults>), Error>
771        {
772            let (launcher_proxy, server_end) = create_proxy::<fproc::LauncherMarker>();
773            let (sender, receiver) = oneshot::channel();
774            fasync::Task::local(async move {
775                let stream = server_end.into_stream();
776                run_launcher_service(stream, sender)
777                    .await
778                    .expect("error running fake launcher service");
779            })
780            .detach();
781            Ok((launcher_proxy, receiver))
782        }
783
784        async fn run_launcher_service(
785            mut stream: fproc::LauncherRequestStream,
786            sender: oneshot::Sender<FakeLauncherServiceResults>,
787        ) -> Result<(), Error> {
788            let mut res = FakeLauncherServiceResults::default();
789            while let Some(event) = stream.try_next().await? {
790                match event {
791                    fproc::LauncherRequest::AddArgs { args, .. } => {
792                        res.args.extend(
793                            args.into_iter()
794                                .map(|a| {
795                                    std::str::from_utf8(&a)
796                                        .expect("cannot convert bytes to utf8 string")
797                                        .to_owned()
798                                })
799                                .collect::<Vec<String>>(),
800                        );
801                    }
802                    fproc::LauncherRequest::AddEnvirons { .. } => {}
803                    fproc::LauncherRequest::AddNames { names, .. } => {
804                        res.names
805                            .extend(names.into_iter().map(|m| m.path).collect::<Vec<String>>());
806                    }
807                    fproc::LauncherRequest::AddHandles { handles, .. } => {
808                        res.handles.extend(handles.into_iter().map(|m| m.id).collect::<Vec<u32>>());
809                    }
810                    fproc::LauncherRequest::SetOptions { options, .. } => {
811                        res.options = zx::ProcessOptions::from_bits_retain(options);
812                    }
813                    fproc::LauncherRequest::CreateWithoutStarting { .. } => {}
814                    fproc::LauncherRequest::Launch { .. } => {}
815                }
816            }
817            sender.send(res).map_err(|_e| format_err!("can't send result"))?;
818            Ok(())
819        }
820
821        #[fuchsia::test]
822        async fn missing_pkg() -> Result<(), Error> {
823            let (launcher_proxy, _server_end) = create_proxy::<fproc::LauncherMarker>();
824            let ns = setup_empty_namespace()?;
825
826            assert_eq!(
827                configure_launcher(LauncherConfigArgs {
828                    bin_path: "bin/path",
829                    name: "name",
830                    args: None,
831                    options: zx::ProcessOptions::empty(),
832                    ns: ns,
833                    job: None,
834                    handle_infos: None,
835                    name_infos: None,
836                    environs: None,
837                    launcher: &launcher_proxy,
838                    loader_proxy_chan: None,
839                    executable_vmo: None
840                })
841                .await,
842                Err(LaunchError::MissingPkg),
843            );
844
845            drop(_server_end);
846            Ok(())
847        }
848
849        #[fuchsia::test]
850        async fn invalid_executable() -> Result<(), Error> {
851            let (launcher_proxy, _server_end) = create_proxy::<fproc::LauncherMarker>();
852            let ns = setup_namespace(true, vec![])?;
853
854            match configure_launcher(LauncherConfigArgs {
855                bin_path: "test/path",
856                name: "name",
857                args: None,
858                options: zx::ProcessOptions::empty(),
859                ns: ns,
860                job: None,
861                handle_infos: None,
862                name_infos: None,
863                environs: None,
864                launcher: &launcher_proxy,
865                loader_proxy_chan: None,
866                executable_vmo: None,
867            })
868            .await
869            .expect_err("should error out")
870            {
871                LaunchError::LoadingExecutable(_) => {}
872                e => panic!("Expected LoadingExecutable error, got {:?}", e),
873            }
874            Ok(())
875        }
876
877        #[fuchsia::test]
878        async fn invalid_pkg() -> Result<(), Error> {
879            let (launcher_proxy, _server_end) = create_proxy::<fproc::LauncherMarker>();
880            let ns = setup_namespace(false, vec!["/pkg"])?;
881
882            match configure_launcher(LauncherConfigArgs {
883                bin_path: "bin/path",
884                name: "name",
885                args: None,
886                options: zx::ProcessOptions::empty(),
887                ns: ns,
888                job: None,
889                handle_infos: None,
890                name_infos: None,
891                environs: None,
892                launcher: &launcher_proxy,
893                loader_proxy_chan: None,
894                executable_vmo: None,
895            })
896            .await
897            .expect_err("should error out")
898            {
899                LaunchError::LoadingExecutable(_) => {}
900                e => panic!("Expected LoadingExecutable error, got {:?}", e),
901            }
902            Ok(())
903        }
904
905        #[fuchsia::test]
906        async fn default_args() -> Result<(), Error> {
907            let (launcher_proxy, recv) = start_launcher()?;
908
909            let ns = setup_namespace(true, vec![])?;
910
911            let _launch_info = configure_launcher(LauncherConfigArgs {
912                bin_path: "bin/runner_lib_test",
913                name: "name",
914                args: None,
915                options: zx::ProcessOptions::empty(),
916                ns: ns,
917                job: None,
918                handle_infos: None,
919                name_infos: None,
920                environs: None,
921                launcher: &launcher_proxy,
922                loader_proxy_chan: None,
923                executable_vmo: None,
924            })
925            .await?;
926
927            drop(launcher_proxy);
928
929            let ls = recv.await?;
930
931            assert_eq!(ls.args, vec!("/pkg/bin/runner_lib_test".to_owned()));
932
933            Ok(())
934        }
935
936        #[fasync::run_singlethreaded(test)]
937        async fn custom_executable_vmo() -> Result<(), Error> {
938            let (launcher_proxy, _recv) = start_launcher()?;
939
940            let ns = setup_namespace(true, vec![])?;
941            let vmo = zx::Vmo::create(100)?;
942            vmo.write(b"my_data", 0)?;
943            let launch_info = configure_launcher(LauncherConfigArgs {
944                bin_path: "bin/runner_lib_test",
945                name: "name",
946                args: None,
947                options: zx::ProcessOptions::empty(),
948                ns: ns,
949                job: None,
950                handle_infos: None,
951                name_infos: None,
952                environs: None,
953                launcher: &launcher_proxy,
954                loader_proxy_chan: None,
955                executable_vmo: Some(vmo),
956            })
957            .await?;
958
959            let mut bytes: [u8; 10] = [0; 10];
960            launch_info.executable.read(&mut bytes, 0)?;
961            let expected = b"my_data";
962            assert_eq!(bytes[0..expected.len()], expected[..]);
963            Ok(())
964        }
965
966        #[fasync::run_singlethreaded(test)]
967        async fn extra_args() -> Result<(), Error> {
968            let (launcher_proxy, recv) = start_launcher()?;
969
970            let ns = setup_namespace(true, vec![])?;
971
972            let args = vec!["args1".to_owned(), "arg2".to_owned()];
973
974            let _launch_info = configure_launcher(LauncherConfigArgs {
975                bin_path: "bin/runner_lib_test",
976                name: "name",
977                args: Some(args.clone()),
978                options: zx::ProcessOptions::empty(),
979                ns: ns,
980                job: None,
981                handle_infos: None,
982                name_infos: None,
983                environs: None,
984                launcher: &launcher_proxy,
985                loader_proxy_chan: None,
986                executable_vmo: None,
987            })
988            .await?;
989
990            drop(launcher_proxy);
991
992            let ls = recv.await?;
993
994            let mut expected = vec!["/pkg/bin/runner_lib_test".to_owned()];
995            expected.extend(args);
996            assert_eq!(ls.args, expected);
997
998            Ok(())
999        }
1000
1001        #[fasync::run_singlethreaded(test)]
1002        async fn namespace_added() -> Result<(), Error> {
1003            let (launcher_proxy, recv) = start_launcher()?;
1004
1005            let ns = setup_namespace(true, vec!["/some_path1", "/some_path2"])?;
1006
1007            let _launch_info = configure_launcher(LauncherConfigArgs {
1008                bin_path: "bin/runner_lib_test",
1009                name: "name",
1010                args: None,
1011                options: zx::ProcessOptions::empty(),
1012                ns: ns,
1013                job: None,
1014                handle_infos: None,
1015                name_infos: None,
1016                environs: None,
1017                launcher: &launcher_proxy,
1018                loader_proxy_chan: None,
1019                executable_vmo: None,
1020            })
1021            .await?;
1022
1023            drop(launcher_proxy);
1024
1025            let ls = recv.await?;
1026
1027            let mut names = ls.names;
1028            names.sort();
1029            assert_eq!(
1030                names,
1031                vec!("/pkg", "/some_path1", "/some_path2")
1032                    .into_iter()
1033                    .map(|s| s.to_string())
1034                    .collect::<Vec<String>>()
1035            );
1036
1037            Ok(())
1038        }
1039
1040        #[fasync::run_singlethreaded(test)]
1041        async fn extra_namespace_entries() -> Result<(), Error> {
1042            let (launcher_proxy, recv) = start_launcher()?;
1043
1044            let ns = setup_namespace(true, vec!["/some_path1", "/some_path2"])?;
1045
1046            let mut names = vec![];
1047
1048            let extra_paths = vec!["/extra1", "/extra2"];
1049
1050            for path in &extra_paths {
1051                let (client, _server) = create_endpoints::<fio::DirectoryMarker>();
1052
1053                names.push(fproc::NameInfo { path: path.to_string(), directory: client });
1054            }
1055
1056            let _launch_info = configure_launcher(LauncherConfigArgs {
1057                bin_path: "bin/runner_lib_test",
1058                name: "name",
1059                args: None,
1060                options: zx::ProcessOptions::empty(),
1061                ns: ns,
1062                job: None,
1063                handle_infos: None,
1064                name_infos: Some(names),
1065                environs: None,
1066                launcher: &launcher_proxy,
1067                loader_proxy_chan: None,
1068                executable_vmo: None,
1069            })
1070            .await?;
1071
1072            drop(launcher_proxy);
1073
1074            let ls = recv.await?;
1075
1076            let mut paths = vec!["/pkg", "/some_path1", "/some_path2"];
1077            paths.extend(extra_paths.into_iter());
1078            paths.sort();
1079
1080            let mut ls_names = ls.names;
1081            ls_names.sort();
1082
1083            assert_eq!(ls_names, paths.into_iter().map(|s| s.to_string()).collect::<Vec<String>>());
1084
1085            Ok(())
1086        }
1087
1088        #[fasync::run_singlethreaded(test)]
1089        async fn handles_added() -> Result<(), Error> {
1090            let (launcher_proxy, recv) = start_launcher()?;
1091
1092            let ns = setup_namespace(true, vec![])?;
1093
1094            let _launch_info = configure_launcher(LauncherConfigArgs {
1095                bin_path: "bin/runner_lib_test",
1096                name: "name",
1097                args: None,
1098                options: zx::ProcessOptions::empty(),
1099                ns: ns,
1100                job: None,
1101                handle_infos: None,
1102                name_infos: None,
1103                environs: None,
1104                launcher: &launcher_proxy,
1105                loader_proxy_chan: None,
1106                executable_vmo: None,
1107            })
1108            .await?;
1109
1110            drop(launcher_proxy);
1111
1112            let ls = recv.await?;
1113
1114            assert_eq!(
1115                ls.handles,
1116                vec!(
1117                    HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
1118                    HandleInfo::new(HandleType::DefaultJob, 0).as_raw()
1119                )
1120            );
1121
1122            Ok(())
1123        }
1124
1125        #[fasync::run_singlethreaded(test)]
1126        async fn handles_added_with_custom_loader_chan() -> Result<(), Error> {
1127            let (launcher_proxy, recv) = start_launcher()?;
1128
1129            let (c1, _c2) = zx::Channel::create();
1130
1131            let ns = setup_namespace(true, vec![])?;
1132
1133            let _launch_info = configure_launcher(LauncherConfigArgs {
1134                bin_path: "bin/runner_lib_test",
1135                name: "name",
1136                args: None,
1137                options: zx::ProcessOptions::empty(),
1138                ns: ns,
1139                job: None,
1140                handle_infos: None,
1141                name_infos: None,
1142                environs: None,
1143                launcher: &launcher_proxy,
1144                loader_proxy_chan: Some(c1),
1145                executable_vmo: None,
1146            })
1147            .await?;
1148
1149            drop(launcher_proxy);
1150
1151            let ls = recv.await?;
1152
1153            assert_eq!(
1154                ls.handles,
1155                vec!(
1156                    HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
1157                    HandleInfo::new(HandleType::DefaultJob, 0).as_raw()
1158                )
1159            );
1160
1161            Ok(())
1162        }
1163
1164        #[fasync::run_singlethreaded(test)]
1165        async fn extra_handles() -> Result<(), Error> {
1166            let (launcher_proxy, recv) = start_launcher()?;
1167
1168            let ns = setup_namespace(true, vec![])?;
1169
1170            let mut handle_infos = vec![];
1171            for fd in 0..3 {
1172                let (client, _server) = create_endpoints::<fio::DirectoryMarker>();
1173                handle_infos.push(fproc::HandleInfo {
1174                    handle: client.into_channel().into_handle(),
1175                    id: fd,
1176                });
1177            }
1178
1179            let _launch_info = configure_launcher(LauncherConfigArgs {
1180                bin_path: "bin/runner_lib_test",
1181                name: "name",
1182                args: None,
1183                options: zx::ProcessOptions::empty(),
1184                ns: ns,
1185                job: None,
1186                handle_infos: Some(handle_infos),
1187                name_infos: None,
1188                environs: None,
1189                launcher: &launcher_proxy,
1190                loader_proxy_chan: None,
1191                executable_vmo: None,
1192            })
1193            .await?;
1194
1195            drop(launcher_proxy);
1196
1197            let ls = recv.await?;
1198
1199            assert_eq!(
1200                ls.handles,
1201                vec!(
1202                    0,
1203                    1,
1204                    2,
1205                    HandleInfo::new(HandleType::LdsvcLoader, 0).as_raw(),
1206                    HandleInfo::new(HandleType::DefaultJob, 0).as_raw(),
1207                )
1208            );
1209
1210            Ok(())
1211        }
1212    }
1213}