1use crate::cli::format::{
6 format_create_error, format_destroy_error, format_resolve_error, format_start_error,
7};
8use crate::lifecycle::{
9 create_instance_in_collection, destroy_instance_in_collection, resolve_instance,
10 start_instance, start_instance_with_args, ActionError, CreateError, DestroyError, StartError,
11};
12use anyhow::{bail, format_err, Result};
13#[allow(unused)]
14use flex_client::ProxyHasDomain;
15use flex_client::{HandleBased, Socket};
16use fuchsia_url::AbsoluteComponentUrl;
17use futures::future::BoxFuture;
18#[allow(unused)]
19use futures::{AsyncReadExt, AsyncWriteExt};
20use moniker::Moniker;
21use std::io::Read;
22use {
23 flex_fuchsia_component as fcomponent, flex_fuchsia_component_decl as fdecl,
24 flex_fuchsia_process as fprocess, flex_fuchsia_sys2 as fsys,
25};
26
27const TRANSFER_CHUNK_SIZE: usize = 8192;
31
32async fn copy<W: std::io::Write>(source: Socket, mut sink: W) -> Result<()> {
33 #[cfg(not(feature = "fdomain"))]
34 let mut source = fuchsia_async::Socket::from_socket(source);
35 let mut buf = [0u8; TRANSFER_CHUNK_SIZE];
36 loop {
37 let bytes_read = source.read(&mut buf).await?;
38 if bytes_read == 0 {
39 return Ok(());
40 }
41 sink.write_all(&buf[..bytes_read])?;
42 sink.flush()?;
43 }
44}
45
46fn handle_id_for_fd(fd: u32) -> u32 {
50 const PA_FD: u32 = 0x30;
51 PA_FD | fd << 16
52}
53
54struct Stdio {
55 local_in: Socket,
56 local_out: Socket,
57 local_err: Socket,
58}
59
60impl Stdio {
61 #[cfg(not(feature = "fdomain"))]
62 fn new() -> (Self, Vec<fprocess::HandleInfo>) {
63 let (local_in, remote_in) = fidl::Socket::create_stream();
64 let (local_out, remote_out) = fidl::Socket::create_stream();
65 let (local_err, remote_err) = fidl::Socket::create_stream();
66
67 (
68 Self { local_in, local_out, local_err },
69 vec![
70 fprocess::HandleInfo { handle: remote_in.into_handle(), id: handle_id_for_fd(0) },
71 fprocess::HandleInfo { handle: remote_out.into_handle(), id: handle_id_for_fd(1) },
72 fprocess::HandleInfo { handle: remote_err.into_handle(), id: handle_id_for_fd(2) },
73 ],
74 )
75 }
76
77 #[cfg(feature = "fdomain")]
78 fn new(client: &std::sync::Arc<flex_client::Client>) -> (Self, Vec<fprocess::HandleInfo>) {
79 let (local_in, remote_in) = client.create_stream_socket();
80 let (local_out, remote_out) = client.create_stream_socket();
81 let (local_err, remote_err) = client.create_stream_socket();
82
83 (
84 Self { local_in, local_out, local_err },
85 vec![
86 fprocess::HandleInfo { handle: remote_in.into_handle(), id: handle_id_for_fd(0) },
87 fprocess::HandleInfo { handle: remote_out.into_handle(), id: handle_id_for_fd(1) },
88 fprocess::HandleInfo { handle: remote_err.into_handle(), id: handle_id_for_fd(2) },
89 ],
90 )
91 }
92
93 async fn forward(self) {
94 let local_in = self.local_in;
95 let local_out = self.local_out;
96 let local_err = self.local_err;
97
98 #[cfg(not(feature = "fdomain"))]
99 let mut local_in = fuchsia_async::Socket::from_socket(local_in);
100
101 std::thread::spawn(move || {
102 let mut term_in = std::io::stdin().lock();
103 let mut buf = [0u8; TRANSFER_CHUNK_SIZE];
104 let mut executor = fuchsia_async::LocalExecutor::new();
105 loop {
106 let bytes_read = term_in.read(&mut buf)?;
107 if bytes_read == 0 {
108 return Ok::<(), anyhow::Error>(());
109 }
110
111 executor.run_singlethreaded(local_in.write_all(&buf[..bytes_read]))?;
112 }
113 });
114
115 std::thread::spawn(move || {
116 let mut executor = fuchsia_async::LocalExecutor::new();
117 let _result: Result<()> = executor
118 .run_singlethreaded(async move { copy(local_err, std::io::stderr()).await });
119 });
120
121 std::thread::spawn(move || {
122 let mut executor = fuchsia_async::LocalExecutor::new();
123 let _result: Result<()> = executor
124 .run_singlethreaded(async move { copy(local_out, std::io::stdout().lock()).await });
125 std::process::exit(0);
126 });
127
128 let () = futures::future::pending().await;
131 }
132}
133
134pub async fn run_cmd<W: std::io::Write>(
135 moniker: Moniker,
136 url: AbsoluteComponentUrl,
137 recreate: bool,
138 connect_stdio: bool,
139 config_overrides: Vec<fdecl::ConfigOverride>,
140 lifecycle_controller_factory: impl Fn()
141 -> BoxFuture<'static, Result<fsys::LifecycleControllerProxy>>,
142 mut writer: W,
143) -> Result<()> {
144 let lifecycle_controller = lifecycle_controller_factory().await?;
145 let parent = moniker
146 .parent()
147 .ok_or_else(|| format_err!("Error: {} does not reference a dynamic instance", moniker))?;
148 let leaf = moniker
149 .leaf()
150 .ok_or_else(|| format_err!("Error: {} does not reference a dynamic instance", moniker))?;
151 let child_name = leaf.name();
152 let collection = leaf
153 .collection()
154 .ok_or_else(|| format_err!("Error: {} does not reference a dynamic instance", moniker))?;
155
156 if recreate {
157 match destroy_instance_in_collection(&lifecycle_controller, &parent, collection, child_name)
159 .await
160 {
161 Ok(()) => {
162 writeln!(writer, "Destroyed existing component instance at {}...", moniker)?;
163 }
164 Err(DestroyError::ActionError(ActionError::InstanceNotFound))
165 | Err(DestroyError::ActionError(ActionError::InstanceNotResolved)) => {
166 }
168 Err(e) => return Err(format_destroy_error(&moniker, e)),
169 }
170 }
171
172 writeln!(writer, "URL: {}", url)?;
173 writeln!(writer, "Moniker: {}", moniker)?;
174 writeln!(writer, "Creating component instance...")?;
175
176 let (mut maybe_stdio, numbered_handles) = if connect_stdio {
179 #[cfg(not(feature = "fdomain"))]
180 let (stdio, numbered_handles) = Stdio::new();
181 #[cfg(feature = "fdomain")]
182 let (stdio, numbered_handles) = Stdio::new(&lifecycle_controller.domain());
183 (Some(stdio), Some(numbered_handles))
184 } else {
185 (None, Some(vec![]))
186 };
187
188 let create_result = create_instance_in_collection(
189 &lifecycle_controller,
190 &parent,
191 collection,
192 child_name,
193 &url,
194 config_overrides.clone(),
195 None,
196 )
197 .await;
198
199 match create_result {
200 Err(CreateError::InstanceAlreadyExists) => {
201 bail!("\nError: {} already exists.\nUse --recreate to destroy and create a new instance, or provide a different moniker.\n", moniker)
202 }
203 Err(e) => {
204 return Err(format_create_error(&moniker, &parent, collection, e));
205 }
206 Ok(()) => {}
207 }
208
209 writeln!(writer, "Resolving component instance...")?;
210 resolve_instance(&lifecycle_controller, &moniker)
211 .await
212 .map_err(|e| format_resolve_error(&moniker, e))?;
213
214 writeln!(writer, "Starting component instance...")?;
215 let start_args = fcomponent::StartChildArgs { numbered_handles, ..Default::default() };
216 let res = start_instance_with_args(&lifecycle_controller, &moniker, start_args).await;
217 if let Err(StartError::ActionError(ActionError::Fidl(_e))) = &res {
218 let lifecycle_controller = lifecycle_controller_factory().await?;
225
226 if connect_stdio {
227 #[cfg(not(feature = "fdomain"))]
232 let (stdio, numbered_handles) = Stdio::new();
233 #[cfg(feature = "fdomain")]
234 let (stdio, numbered_handles) = Stdio::new(&lifecycle_controller.domain());
235 maybe_stdio = Some(stdio);
236 let create_args = fcomponent::CreateChildArgs {
237 numbered_handles: Some(numbered_handles),
238 ..Default::default()
239 };
240
241 destroy_instance_in_collection(&lifecycle_controller, &parent, collection, child_name)
242 .await?;
243 create_instance_in_collection(
244 &lifecycle_controller,
245 &parent,
246 collection,
247 child_name,
248 &url,
249 config_overrides,
250 Some(create_args),
251 )
252 .await?;
253 resolve_instance(&lifecycle_controller, &moniker)
254 .await
255 .map_err(|e| format_resolve_error(&moniker, e))?;
256 }
257
258 let _stop_future = start_instance(&lifecycle_controller, &moniker)
259 .await
260 .map_err(|e| format_start_error(&moniker, e))?;
261 } else {
262 let _stop_future = res.map_err(|e| format_start_error(&moniker, e))?;
263 }
264
265 if let Some(stdio) = maybe_stdio {
266 stdio.forward().await;
267 }
268
269 writeln!(writer, "Component instance is running!")?;
270
271 Ok(())
272}
273
274#[cfg(test)]
275mod test {
276 use super::*;
277 use fidl::endpoints::create_proxy_and_stream;
278 use flex_fuchsia_sys2 as fsys;
279 use futures::{FutureExt, TryStreamExt};
280
281 fn setup_fake_lifecycle_controller_ok(
282 expected_parent_moniker: &'static str,
283 expected_collection: &'static str,
284 expected_name: &'static str,
285 expected_url: &'static str,
286 expected_moniker: &'static str,
287 expect_destroy: bool,
288 ) -> fsys::LifecycleControllerProxy {
289 let (lifecycle_controller, mut stream) =
290 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
291 fuchsia_async::Task::local(async move {
292 if expect_destroy {
293 let req = stream.try_next().await.unwrap().unwrap();
294 match req {
295 fsys::LifecycleControllerRequest::DestroyInstance {
296 parent_moniker,
297 child,
298 responder,
299 } => {
300 assert_eq!(
301 Moniker::parse_str(expected_parent_moniker),
302 Moniker::parse_str(&parent_moniker)
303 );
304 assert_eq!(expected_name, child.name);
305 assert_eq!(expected_collection, child.collection.unwrap());
306 responder.send(Ok(())).unwrap();
307 }
308 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
309 }
310 }
311
312 let req = stream.try_next().await.unwrap().unwrap();
313 match req {
314 fsys::LifecycleControllerRequest::CreateInstance {
315 parent_moniker,
316 collection,
317 decl,
318 responder,
319 args: _,
320 } => {
321 assert_eq!(
322 Moniker::parse_str(expected_parent_moniker),
323 Moniker::parse_str(&parent_moniker)
324 );
325 assert_eq!(expected_collection, collection.name);
326 assert_eq!(expected_name, decl.name.unwrap());
327 assert_eq!(expected_url, decl.url.unwrap());
328 responder.send(Ok(())).unwrap();
329 }
330 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
331 }
332
333 let req = stream.try_next().await.unwrap().unwrap();
334 match req {
335 fsys::LifecycleControllerRequest::ResolveInstance { moniker, responder } => {
336 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
337 responder.send(Ok(())).unwrap();
338 }
339 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
340 }
341
342 let req = stream.try_next().await.unwrap().unwrap();
343 match req {
344 fsys::LifecycleControllerRequest::StartInstanceWithArgs {
345 moniker,
346 binder: _,
347 args: _,
348 responder,
349 } => {
350 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
351 responder.send(Ok(())).unwrap();
352 }
353 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
354 }
355 })
356 .detach();
357 lifecycle_controller
358 }
359
360 fn setup_fake_lifecycle_controller_fail(
361 expected_parent_moniker: &'static str,
362 expected_collection: &'static str,
363 expected_name: &'static str,
364 expected_url: &'static str,
365 ) -> fsys::LifecycleControllerProxy {
366 let (lifecycle_controller, mut stream) =
367 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
368 fuchsia_async::Task::local(async move {
369 let req = stream.try_next().await.unwrap().unwrap();
370 match req {
371 fsys::LifecycleControllerRequest::DestroyInstance {
372 parent_moniker,
373 child,
374 responder,
375 } => {
376 assert_eq!(
377 Moniker::parse_str(expected_parent_moniker),
378 Moniker::parse_str(&parent_moniker)
379 );
380 assert_eq!(expected_name, child.name);
381 assert_eq!(expected_collection, child.collection.unwrap());
382 responder.send(Ok(())).unwrap();
383 }
384 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
385 }
386
387 let req = stream.try_next().await.unwrap().unwrap();
388 match req {
389 fsys::LifecycleControllerRequest::CreateInstance {
390 parent_moniker,
391 collection,
392 decl,
393 responder,
394 args: _,
395 } => {
396 assert_eq!(
397 Moniker::parse_str(expected_parent_moniker),
398 Moniker::parse_str(&parent_moniker)
399 );
400 assert_eq!(expected_collection, collection.name);
401 assert_eq!(expected_name, decl.name.unwrap());
402 assert_eq!(expected_url, decl.url.unwrap());
403 responder.send(Err(fsys::CreateError::InstanceAlreadyExists)).unwrap();
404 }
405 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
406 }
407 })
408 .detach();
409 lifecycle_controller
410 }
411
412 fn setup_fake_lifecycle_controller_recreate(
413 expected_parent_moniker: &'static str,
414 expected_collection: &'static str,
415 expected_name: &'static str,
416 expected_url: &'static str,
417 expected_moniker: &'static str,
418 ) -> fsys::LifecycleControllerProxy {
419 let (lifecycle_controller, mut stream) =
420 create_proxy_and_stream::<fsys::LifecycleControllerMarker>();
421 fuchsia_async::Task::local(async move {
422 let req = stream.try_next().await.unwrap().unwrap();
423 match req {
424 fsys::LifecycleControllerRequest::DestroyInstance {
425 parent_moniker,
426 child,
427 responder,
428 } => {
429 assert_eq!(
430 Moniker::parse_str(expected_parent_moniker),
431 Moniker::parse_str(&parent_moniker)
432 );
433 assert_eq!(expected_name, child.name);
434 assert_eq!(expected_collection, child.collection.unwrap());
435 responder.send(Ok(())).unwrap();
436 }
437 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
438 }
439
440 let req = stream.try_next().await.unwrap().unwrap();
441 match req {
442 fsys::LifecycleControllerRequest::CreateInstance {
443 parent_moniker,
444 collection,
445 decl,
446 responder,
447 args: _,
448 } => {
449 assert_eq!(
450 Moniker::parse_str(expected_parent_moniker),
451 Moniker::parse_str(&parent_moniker)
452 );
453 assert_eq!(expected_collection, collection.name);
454 assert_eq!(expected_name, decl.name.unwrap());
455 assert_eq!(expected_url, decl.url.unwrap());
456 responder.send(Ok(())).unwrap();
457 }
458 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
459 }
460
461 let req = stream.try_next().await.unwrap().unwrap();
462 match req {
463 fsys::LifecycleControllerRequest::ResolveInstance { moniker, responder } => {
464 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
465 responder.send(Ok(())).unwrap();
466 }
467 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
468 }
469
470 let req = stream.try_next().await.unwrap().unwrap();
471 match req {
472 fsys::LifecycleControllerRequest::StartInstanceWithArgs {
473 moniker,
474 binder: _,
475 args: _,
476 responder,
477 } => {
478 assert_eq!(Moniker::parse_str(expected_moniker), Moniker::parse_str(&moniker));
479 responder.send(Ok(())).unwrap();
480 }
481 _ => panic!("Unexpected Lifecycle Controller request: {:?}", req),
482 }
483 })
484 .detach();
485 lifecycle_controller
486 }
487
488 #[fuchsia_async::run_singlethreaded(test)]
489 async fn test_ok() -> Result<()> {
490 let mut output = Vec::new();
491 let lifecycle_controller = setup_fake_lifecycle_controller_ok(
492 "/some",
493 "collection",
494 "name",
495 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
496 "/some/collection:name",
497 true,
498 );
499 let response = run_cmd(
500 "/some/collection:name".try_into().unwrap(),
501 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
502 true,
503 false,
504 vec![],
505 move || {
506 let lifecycle_controller = lifecycle_controller.clone();
507 async move { Ok(lifecycle_controller) }.boxed()
508 },
509 &mut output,
510 )
511 .await;
512 response.unwrap();
513 Ok(())
514 }
515
516 #[fuchsia_async::run_singlethreaded(test)]
517 async fn test_name() -> Result<()> {
518 let mut output = Vec::new();
519 let lifecycle_controller = setup_fake_lifecycle_controller_ok(
520 "/core",
521 "ffx-laboratory",
522 "foobar",
523 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
524 "/core/ffx-laboratory:foobar",
525 false,
526 );
527 let response = run_cmd(
528 "/core/ffx-laboratory:foobar".try_into().unwrap(),
529 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
530 false,
531 false,
532 vec![],
533 move || {
534 let lifecycle_controller = lifecycle_controller.clone();
535 async move { Ok(lifecycle_controller) }.boxed()
536 },
537 &mut output,
538 )
539 .await;
540 response.unwrap();
541 Ok(())
542 }
543
544 #[fuchsia_async::run_singlethreaded(test)]
545 async fn test_fail() -> Result<()> {
546 let mut output = Vec::new();
547 let lifecycle_controller = setup_fake_lifecycle_controller_fail(
548 "/core",
549 "ffx-laboratory",
550 "test",
551 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
552 );
553 let response = run_cmd(
554 "/core/ffx-laboratory:test".try_into().unwrap(),
555 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
556 true,
557 false,
558 vec![],
559 move || {
560 let lifecycle_controller = lifecycle_controller.clone();
561 async move { Ok(lifecycle_controller) }.boxed()
562 },
563 &mut output,
564 )
565 .await;
566 response.unwrap_err();
567 Ok(())
568 }
569
570 #[fuchsia_async::run_singlethreaded(test)]
571 async fn test_recreate() -> Result<()> {
572 let mut output = Vec::new();
573 let lifecycle_controller = setup_fake_lifecycle_controller_recreate(
574 "/core",
575 "ffx-laboratory",
576 "test",
577 "fuchsia-pkg://fuchsia.com/test#meta/test.cm",
578 "/core/ffx-laboratory:test",
579 );
580 let response = run_cmd(
581 "/core/ffx-laboratory:test".try_into().unwrap(),
582 "fuchsia-pkg://fuchsia.com/test#meta/test.cm".try_into().unwrap(),
583 true,
584 false,
585 vec![],
586 move || {
587 let lifecycle_controller = lifecycle_controller.clone();
588 async move { Ok(lifecycle_controller) }.boxed()
589 },
590 &mut output,
591 )
592 .await;
593 response.unwrap();
594 Ok(())
595 }
596}