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