blob: e78b80fca7fbb84a8b5927fe3aed49fed4fb0249 [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 {
anyhow::{format_err, Context as _, Error},
fidl_fuchsia_netemul_example::{CounterMarker, CounterRequest, CounterRequestStream},
fidl_fuchsia_sys::{
ComponentControllerEvent, ComponentControllerMarker, ComponentControllerProxy,
EnvironmentControllerMarker, EnvironmentMarker, EnvironmentOptions, EnvironmentProxy,
LaunchInfo, LauncherMarker, TerminationReason,
},
fuchsia_async as fasync,
fuchsia_component::client,
fuchsia_component::server::ServiceFs,
futures::prelude::*,
std::sync::Arc,
std::sync::Mutex,
structopt::StructOpt,
};
const MY_PACKAGE: &str = "fuchsia-pkg://fuchsia.com/netemul-sandbox-test#meta/inheritance.cmx";
#[derive(StructOpt, Debug)]
enum Ops {
#[structopt(name = "serve")]
Serve,
#[structopt(name = "inherit")]
Inherit,
#[structopt(name = "no-inherit")]
NoInherit,
#[structopt(name = "same-instance")]
SameInstance { expect: u32 },
}
#[derive(StructOpt, Debug)]
struct Opt {
#[structopt(subcommand)]
mode: Option<Ops>,
}
async fn simple_increment(expect: u32) -> Result<(), Error> {
let counter = client::connect_to_service::<CounterMarker>()?;
let value = counter.increment().await?;
if value != expect {
return Err(format_err!("unexpected counter value {}, was expecting {}", value, expect));
}
return Ok(());
}
async fn wait_for_component(component: &ComponentControllerProxy) -> Result<(), Error> {
let mut component_events = component.take_event_stream();
// wait for child to exit and mimic the result code
let result = loop {
let event = component_events
.try_next()
.await
.context("wait for child component to exit")?
.ok_or_else(|| format_err!("Child didn't exit cleanly"))?;
match event {
ComponentControllerEvent::OnTerminated {
return_code: code,
termination_reason: reason,
} => {
if code != 0 || reason != TerminationReason::Exited {
break Err(format_err!(
"Child exited with code {}, reason {}",
code,
reason as u32
));
} else {
break Ok(());
}
}
_ => {
continue;
}
}
};
result
}
async fn spawn_child_and_expect(env: EnvironmentProxy, expect: u32) -> Result<(), Error> {
// connect to fuchsia.sys.Launcher:
let (launcher, lch) = fidl::endpoints::create_proxy::<LauncherMarker>()?;
let () = env.get_launcher(lch)?;
let expect = format!("{}", expect);
let mut linfo = LaunchInfo {
url: String::from(MY_PACKAGE),
arguments: Some(vec![String::from("same-instance"), String::from(expect)]),
additional_services: None,
directory_request: None,
err: None,
out: None,
flat_namespace: None,
};
let (comp_controller, comp_controller_req) =
fidl::endpoints::create_proxy::<ComponentControllerMarker>()?;
let () = launcher.create_component(&mut linfo, Some(comp_controller_req))?;
let () = wait_for_component(&comp_controller).await?;
Ok(())
}
async fn run_root() -> Result<(), Error> {
// root process should have a brand new instance of the counter service,
// so we should expect the value to be 1 after a first increment:
let () = simple_increment(1).await?;
// not let's try to spawn a process in this environment
// it should have access to exactly the same instance of the counter service,
// hence we're tell it to expect the value "2" on the counter:
let env = client::connect_to_service::<EnvironmentMarker>()?;
let () = spawn_child_and_expect(env, 2).await?;
// finally we can spawn another fidl.sys.Environment from the first one
// with the option to inherit the parent services.
// Then, from that nested environment, we can observe that the same instance is there
// by expecting a new increment of the counter to be "3"
let env = client::connect_to_service::<EnvironmentMarker>()?;
let (child_env, child_env_req) = fidl::endpoints::create_proxy::<EnvironmentMarker>()?;
let (_child_ctlr, child_ctlr_req) =
fidl::endpoints::create_proxy::<EnvironmentControllerMarker>()?;
let mut options = EnvironmentOptions {
inherit_parent_services: true,
use_parent_runners: true,
kill_on_oom: true,
delete_storage_on_death: false,
};
let () = env.create_nested_environment(
child_env_req,
child_ctlr_req,
"same-instance-child",
None,
&mut options,
)?;
let () = spawn_child_and_expect(child_env, 3).await?;
Ok(())
}
async fn run_no_inherit() -> Result<(), Error> {
simple_increment(1)
.await
.expect_err("Shouldn't be able to use service due to inheritance setup");
Ok(())
}
struct CounterData {
value: u32,
}
fn spawn_counter_server(mut stream: CounterRequestStream, data: Arc<Mutex<CounterData>>) {
fasync::Task::spawn(
async move {
while let Some(CounterRequest::Increment { responder }) =
stream.try_next().await.context("error running counter server")?
{
let mut d = data.lock().unwrap();
d.value += 1;
log::info!("Incrementing counter to {}", d.value);
responder.send(d.value).context("Error sending response")?;
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| log::error!("{:?}", e)),
)
.detach()
}
async fn run_server() -> Result<(), Error> {
let data = Arc::new(Mutex::new(CounterData { value: 0 }));
let mut fs = ServiceFs::new();
fs.dir("svc").add_fidl_service(move |chan| spawn_counter_server(chan, data.clone()));
fs.take_and_serve_directory_handle()?;
let () = fs.collect().await;
Ok(())
}
// check this test's .cmx file to see which options are run when.
// the point here is to verify and exemplify the inheritance behavior and interplay
// between fuchsia.netemul.environment.ManagedEnvironment and fuchsia.sys.Environment
// The "root" path has an instance of the Counter service.
// "inherit" and "no-inherit" paths demonstrate how *configuration* inheritance works:
// "inherit" will gain a brand new inherit instance (as seen in the cmx option to inherit the services)
// and "no-inherit" is just verifying that if inheritance is off, the service is not available.
// The "root" path also uses fuchsia.sys.Environment to show how the same *instance* of the Counter
// service can be passed to other processes.
// Observe that ManagedEnvironment will always be hermetic in terms of same instance of services
// inerited from the parent.
#[fasync::run_singlethreaded]
async fn main() -> Result<(), Error> {
let () = fuchsia_syslog::init().context("cannot init logger")?;
let opt = Opt::from_args();
match opt.mode {
None => run_root().await,
Some(Ops::Inherit) => simple_increment(1).await,
Some(Ops::Serve) => run_server().await,
Some(Ops::NoInherit) => run_no_inherit().await,
Some(Ops::SameInstance { expect }) => simple_increment(expect).await,
}
}