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