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