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