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