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