blob: 7c0b7abde7ea389843802ee457df1dc4ced6f77c [file] [log] [blame]
// Copyright 2017 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::sdk::{fuchsia_dir, fx_path, TargetOptions};
use crate::utils::is_mac;
use failure::{bail, err_msg, Error, ResultExt};
use std::env;
use std::path::Path;
use std::process::{Command, Stdio};
use std::{str, thread, time};
pub fn netls(verbose: bool) -> Result<(), Error> {
let fx_script = fx_path()?;
if !fx_script.exists() {
bail!("fx script not found at {:?}", fx_script);
}
let mut netls_command = Command::new(fx_script);
netls_command.arg("netls").arg("--nowait").arg("--timeout=500");
if verbose {
println!("{:?}", netls_command);
}
let netls_status = netls_command.status()?;
if !netls_status.success() {
bail!("netlst failed with error {:?}", netls_status);
}
Ok(())
}
pub fn shell(
verbose: bool,
target_options: &TargetOptions<'_, '_>,
command: &str,
) -> Result<(), Error> {
let fx_script = fx_path()?;
if !fx_script.exists() {
bail!("fx script not found at {:?}", fx_script);
}
let mut ssh_cmd = Command::new(fx_script);
if let Some(device_name) = target_options.device_name {
ssh_cmd.arg(format!("-d={}", device_name));
}
ssh_cmd.arg("shell").arg(command);
ssh_cmd.env("FX_CALLER", "fargo");
if verbose {
println!("ssh_cmd = {:#?}", ssh_cmd);
}
let ssh_result = ssh_cmd.status().context("unable to run fx shell")?;
if !ssh_result.success() {
bail!("ssh failed: {}", ssh_result);
}
Ok(())
}
pub fn setup_network_mac(user: &str) -> Result<(), Error> {
println!("Calling sudo ifconfig to bring up tap0 interface; password may be required.");
let chown_status = Command::new("sudo")
.arg("chown")
.arg(user)
.arg("/dev/tap0")
.status()
.context("couldn't run chown")?;
if !chown_status.success() {
bail!("chown failed: {}", chown_status);
}
let mut loop_count = 0;
loop {
let ifconfig_status = Command::new("sudo")
.arg("ifconfig")
.arg("tap0")
.arg("inet6")
.arg("fc00::/7")
.arg("up")
.status()
.context("couldn't run ifconfig")?;
if !ifconfig_status.success() {
if loop_count > 10 {
bail!("ifconfig failed: {}", ifconfig_status);
}
loop_count += 1;
thread::sleep(time::Duration::from_millis(300));
} else {
break;
}
}
println!("tap0 enabled");
Ok(())
}
#[cfg_attr(rustfmt, rustfmt_skip)]
static TUNCTL_NOT_FOUND_ERROR: &'static str =
"tunctl command not found. Please install uml-utilities.
For help see https://fuchsia.googlesource.com/zircon/+/
master/docs/qemu.md#Enabling-Networking-under-QEMU-x86_64-only";
pub fn setup_network_linux(user: &str) -> Result<(), Error> {
// Create the tap network device if it doesn't exist.
if !Path::new("/sys/class/net/qemu").exists() {
println!(
"Qemu tap device not found. Using sudo and tunctl to create tap network device; \
password may be required."
);
let tunctl_status = Command::new("sudo")
.args(&["tunctl", "-b", "-u", user, "-t", "qemu"])
.stdout(Stdio::null())
.status()
.map_err(|e| {
if e.kind() == ::std::io::ErrorKind::NotFound {
err_msg(TUNCTL_NOT_FOUND_ERROR)
} else {
err_msg("tunctl failed to create a new tap network device")
}
})?;
if !tunctl_status.success() {
bail!("tunctl failed to create tap network device.");
}
}
let ifconfig_status = Command::new("sudo")
.arg("ifconfig")
.arg("qemu")
.arg("up")
.status()
.context("couldn't run ifconfig")?;
if !ifconfig_status.success() {
bail!("ifconfig failed");
}
Ok(())
}
pub fn setup_network() -> Result<(), Error> {
let user = env::var("USER").context("No $USER env var found.")?;
if is_mac() {
setup_network_mac(&user)?;
} else {
setup_network_linux(&user)?;
}
Command::new("stty").arg("sane").status().context("couldn't run stty")?;
Ok(())
}
pub struct StartEmulatorOptions {
pub verbose: bool,
pub aemu: bool,
pub with_graphics: bool,
pub with_acceleration: bool,
pub with_networking: bool,
pub disable_virtcon: bool,
}
pub fn start_emulator(options: &StartEmulatorOptions, params: &[&str]) -> Result<(), Error> {
let fuchsia_dir = fuchsia_dir()?;
let fx_script = fx_path()?;
if !fx_script.exists() {
bail!("fx script not found at {:?}", fx_script);
}
let mut args = vec![];
if options.aemu {
args.push("emu");
} else {
args.push("qemu");
}
if options.with_networking {
args.push("-N");
}
if options.with_graphics {
if !options.aemu {
args.push("-g");
}
} else {
if options.aemu {
args.push("--headless");
}
}
if options.with_acceleration {
if options.aemu {
args.push("-a");
if is_mac() {
args.push("hax");
} else {
args.push("kvm");
}
} else {
args.push("-k");
}
} else {
if options.aemu {
args.push("-a");
args.push("off");
}
}
if options.disable_virtcon {
args.push("-c");
args.push("virtcon.disable");
}
if options.verbose {
println!("fx_script = {:?}", fx_script);
println!("args = {:?}", args);
println!("params = {:?}", params);
}
let child = Command::new(fx_script)
.args(&args)
.args(params)
.stdout(Stdio::null())
.stderr(Stdio::null())
.current_dir(&fuchsia_dir)
.spawn()
.context("unable to run qemu")?;
println!("emulator started with process ID {}", child.id());
if options.with_networking {
setup_network()
} else {
Ok(())
}
}
pub fn stop_emulator() -> Result<(), Error> {
let reg_ex_flag = if is_mac() { "-m" } else { "-r" };
Command::new("killall").arg("-9").arg(reg_ex_flag).arg("qemu-system-x86_64.*").status()?;
Ok(())
}
pub fn enable_networking() -> Result<(), Error> {
setup_network()
}