blob: 473e7caf8cca2cc59e5891f5f334ea0984ed2c4c [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::args::{Ffx, Subcommand},
crate::config::command::exec_config,
crate::constants::{DAEMON, MAX_RETRY_COUNT},
crate::daemon::{is_daemon_running, start as start_daemon},
crate::logger::setup_logger,
anyhow::{format_err, Context, Error},
ffx_run_component::{args::RunComponentCommand, run_component},
ffx_test::{args::TestCommand, test},
fidl::endpoints::{create_proxy, ServiceMarker},
fidl_fuchsia_developer_bridge::{DaemonMarker, DaemonProxy},
fidl_fuchsia_developer_remotecontrol::{RemoteControlMarker, RemoteControlProxy},
fidl_fuchsia_overnet::ServiceConsumerProxyInterface,
fidl_fuchsia_overnet_protocol::NodeId,
std::env,
std::process::Command,
};
mod args;
mod config;
mod constants;
mod daemon;
mod discovery;
mod logger;
mod mdns;
mod net;
mod onet;
mod ssh;
mod target;
mod util;
// Cli
pub struct Cli {
daemon_proxy: DaemonProxy,
}
impl Cli {
pub async fn new() -> Result<Self, Error> {
setup_logger("ffx").await;
let mut peer_id = Cli::find_daemon().await?;
let daemon_proxy = Cli::create_daemon_proxy(&mut peer_id).await?;
Ok(Self { daemon_proxy })
}
pub fn new_with_proxy(daemon_proxy: DaemonProxy) -> Self {
Self { daemon_proxy }
}
async fn create_daemon_proxy(id: &mut NodeId) -> Result<DaemonProxy, Error> {
let svc = hoist::connect_as_service_consumer()?;
let (s, p) = fidl::Channel::create().context("failed to create zx channel")?;
svc.connect_to_service(id, DaemonMarker::NAME, s)?;
let proxy = fidl::AsyncChannel::from_channel(p).context("failed to make async channel")?;
Ok(DaemonProxy::new(proxy))
}
async fn find_daemon() -> Result<NodeId, Error> {
if !is_daemon_running() {
Cli::spawn_daemon().await?;
}
let svc = hoist::connect_as_service_consumer()?;
// Sometimes list_peers doesn't properly report the published services - retry a few times
// but don't loop indefinitely.
for _ in 0..MAX_RETRY_COUNT {
let peers = svc.list_peers().await?;
log::trace!("Got peers: {:?}", peers);
for peer in peers {
if peer.description.services.is_none() {
continue;
}
if peer
.description
.services
.unwrap()
.iter()
.find(|name| *name == DaemonMarker::NAME)
.is_none()
{
continue;
}
return Ok(peer.id);
}
}
panic!("No daemon found.")
}
pub async fn echo(&self, text: Option<String>) -> Result<String, Error> {
match self
.daemon_proxy
.echo_string(match text {
Some(ref t) => t,
None => "Ffx",
})
.await
{
Ok(r) => {
log::info!("SUCCESS: received {:?}", r);
return Ok(r);
}
Err(e) => panic!("ERROR: {:?}", e),
}
}
pub async fn list_targets(&self, text: Option<String>) -> Result<String, Error> {
match self
.daemon_proxy
.list_targets(match text {
Some(ref t) => t,
None => "",
})
.await
{
Ok(r) => {
log::info!("SUCCESS: received {:?}", r);
return Ok(r);
}
Err(e) => panic!("ERROR: {:?}", e),
}
}
pub async fn get_remote_proxy(&self) -> Result<RemoteControlProxy, Error> {
let (remote_proxy, remote_server_end) = create_proxy::<RemoteControlMarker>()?;
let _result = self
.daemon_proxy
.get_remote_control(remote_server_end)
.await
.context("launch_test call failed")
.map_err(|e| format_err!("error getting remote: {:?}", e))?;
Ok(remote_proxy)
}
pub async fn run_component(&mut self, run_cmd: RunComponentCommand) -> Result<(), Error> {
run_component(self.get_remote_proxy().await?, run_cmd).await
}
pub async fn test(&mut self, test_cmd: TestCommand) -> Result<(), Error> {
test(self.get_remote_proxy().await?, test_cmd).await
}
pub async fn quit(&mut self) -> Result<(), Error> {
self.daemon_proxy.quit().await?;
println!("Killed daemon.");
Ok(())
}
async fn spawn_daemon() -> Result<(), Error> {
Command::new(env::current_exe().unwrap()).arg(DAEMON).spawn()?;
Ok(())
}
}
////////////////////////////////////////////////////////////////////////////////
// main
async fn async_main() -> Result<(), Error> {
let app: Ffx = argh::from_env();
let writer = Box::new(std::io::stdout());
match app.subcommand {
Subcommand::Echo(c) => {
match Cli::new().await?.echo(c.text).await {
Ok(r) => {
println!("SUCCESS: received {:?}", r);
}
Err(e) => {
println!("ERROR: {:?}", e);
}
}
Ok(())
}
Subcommand::List(c) => {
match Cli::new().await?.list_targets(c.nodename).await {
Ok(r) => {
let mut r = r.as_str();
if r.is_empty() {
r = "No devices found.";
}
println!("{}", r);
}
Err(e) => {
println!("ERROR: {:?}", e);
}
}
Ok(())
}
Subcommand::RunComponent(c) => {
match Cli::new().await?.run_component(c).await {
Ok(_) => {}
Err(e) => {
println!("ERROR: {:?}", e);
}
}
Ok(())
}
Subcommand::Quit(_) => {
match Cli::new().await?.quit().await {
Ok(_) => {}
Err(e) => {
println!("ERROR: {:?}", e);
}
}
Ok(())
}
Subcommand::Daemon(_) => start_daemon().await,
Subcommand::Config(c) => exec_config(c, writer).await,
Subcommand::Test(t) => {
match Cli::new().await.unwrap().test(t).await {
Ok(_) => {
log::info!("Test successfully run");
}
Err(e) => {
println!("ERROR: {:?}", e);
}
}
Ok(())
}
}
}
fn main() {
hoist::run(async move {
async_main().await.map_err(|e| println!("{}", e)).expect("could not start ffx");
})
}
////////////////////////////////////////////////////////////////////////////////
// tests
#[cfg(test)]
mod test {
use {super::*, fidl_fuchsia_developer_bridge::DaemonRequest, futures::TryStreamExt};
fn setup_fake_daemon_service() -> DaemonProxy {
let (proxy, mut stream) =
fidl::endpoints::create_proxy_and_stream::<DaemonMarker>().unwrap();
hoist::spawn(async move {
while let Ok(req) = stream.try_next().await {
match req {
Some(DaemonRequest::EchoString { value, responder }) => {
let _ = responder.send(value.as_ref());
}
_ => assert!(false),
}
}
});
proxy
}
#[test]
fn test_echo() {
let echo = "test-echo";
hoist::run(async move {
let echoed = Cli::new_with_proxy(setup_fake_daemon_service())
.echo(Some(echo.to_string()))
.await
.unwrap();
assert_eq!(echoed, echo);
});
}
}