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