1use 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#[async_trait]
30pub trait Controllable {
31 async fn kill(&mut self);
34
35 fn stop<'a>(&mut self) -> BoxFuture<'a, ()>;
40
41 fn teardown<'a>(&mut self) -> BoxFuture<'a, ()> {
43 async {}.boxed()
44 }
45
46 #[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
53pub struct Controller<C: Controllable> {
56 request_stream: fcrunner::ComponentControllerRequestStream,
59
60 #[allow(dead_code)] control: fcrunner::ComponentControllerControlHandle,
62
63 controllable: Option<C>,
66
67 #[cfg(fuchsia_api_level_at_least = "HEAD")]
69 on_escrow_monitor: fasync::Task<()>,
70}
71
72#[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 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 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 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 pub async fn serve(mut self, exit_fut: impl Future<Output = StopInfo> + Unpin) {
186 let stop_info = {
187 let request_server = self.serve_controller();
189 futures::pin_mut!(request_server);
190
191 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;
201 }
202 },
203 }
204 };
205
206 if let Some(mut controllable) = self.controllable.take() {
210 controllable.teardown().await;
211 }
212
213 #[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; self.request_stream.control_handle().shutdown();
223 }
224
225 async fn kill(&mut self) {
227 if let Some(mut controllable) = self.controllable.take() {
228 controllable.kill().await;
229 }
230 }
231
232 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#[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
285pub struct LauncherConfigArgs<'a> {
287 pub bin_path: &'a str,
289
290 pub name: &'a str,
293
294 pub options: zx::ProcessOptions,
296
297 pub args: Option<Vec<String>>,
300
301 pub ns: Namespace,
303
304 pub job: Option<zx::Job>,
306
307 pub handle_infos: Option<Vec<fproc::HandleInfo>>,
310
311 pub name_infos: Option<Vec<fproc::NameInfo>>,
313
314 pub environs: Option<Vec<String>>,
316
317 pub launcher: &'a fproc::LauncherProxy,
319
320 pub loader_proxy_chan: Option<zx::Channel>,
322
323 pub executable_vmo: Option<zx::Vmo>,
325}
326
327pub async fn configure_launcher(
331 config_args: LauncherConfigArgs<'_>,
332) -> Result<fproc::LaunchInfo, LaunchError> {
333 let pkg_dir = config_args.ns.get(&PKG_PATH).ok_or(LaunchError::MissingPkg)?;
335
336 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 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 let job = config_args
368 .job
369 .unwrap_or(job_default().create_child_job().map_err(LaunchError::JobCreation)?);
370
371 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 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 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 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
436fn 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
445pub 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 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 controller.serve(term_receiver).await;
545
546 recv.await?;
548
549 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 let controller_serve = fasync::Task::spawn(controller.serve(term_receiver));
598
599 recv.await?;
601
602 teardown_signal_rx.await?;
604
605 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 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 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 client_proxy.stop().expect("FIDL error returned from stop request to controller");
651
652 let term_receiver = Box::pin(async move { term_rx.await.unwrap() });
654 let mut controller_fut = Box::pin(controller.serve(term_receiver));
655
656 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 assert_eq!(exec.run_until_stalled(&mut recv), Poll::Ready(Ok(())));
665
666 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 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 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 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}