component_debug/cli/
run.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
27// This value is fairly arbitrary. The value matches `MAX_BUF` from `fuchsia.io`, but that
28// constant is for `fuchsia.io.File` transfers, which are unrelated to these `zx::socket`
29// transfers.
30const 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
48// The normal Rust representation of this constant is in fuchsia-runtime, which
49// cannot be used on host. Maybe there's a way to move fruntime::HandleType and
50// fruntime::HandleInfo to a place that can be used on host?
51fn 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        // If we're following stdio, we just wait forever. When stdout is
131        // closed, the whole process will exit.
132        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        // First try to destroy any existing instance at this monker.
160        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                // No resolved component exists at this moniker. Nothing to do.
169            }
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    // First try to use StartWithArgs
179
180    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        // A FIDL error here could indicate that we're talking to a version of component manager
224        // that does not support `fuchsia.sys2/LifecycleController.StartInstanceWithArgs`. Let's
225        // try again with `fuchsia.sys2/LifecycleController.StartInstance`.
226
227        // Component manager will close the lifecycle controller when it encounters a FIDL error,
228        // so we need to create a new one.
229        let lifecycle_controller = lifecycle_controller_factory().await?;
230
231        if connect_stdio {
232            // We want to provide stdio handles to the component, but this is only possible when
233            // creating an instance when we have to use the legacy `StartInstance`. Delete and
234            // recreate the component, providing the handles to the create call.
235
236            #[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}