Skip to content

Commit

Permalink
init: Use host $PATH in init.sh
Browse files Browse the repository at this point in the history
Currently all kernel target commands use host environment variables.
However, init.sh uses a hard coded $PATH. This gap is most noticible on
nix/nixos systems b/c nothing exists in the hard coded path. So kernel
target guests are unable to find qemu-ga.

Plug this gap by templatizing init.sh and filling in with host $PATH.

Note init.sh.template now needs to be careful about escaping `{`, as
that's reserved by TinyTemplate now.

This closes danobi#79.
  • Loading branch information
danobi committed Aug 19, 2024
1 parent 5c08c6f commit 468fd04
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 18 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ clap = { version = "4.0.26", features = ["derive", "string"] }
console = "0.15.5"
env_logger = "0.10.0"
itertools = "0.10.5"
lazy_static = "1.4.0"
log = "0.4.17"
qapi = { version = "0.14.0", features = ["qmp", "qga"] }
rand = "0.8.5"
Expand Down
8 changes: 4 additions & 4 deletions src/init/init.sh → src/init/init.sh.template
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

set -eu

export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH={ path }

log() {
log() \{
if [[ -e /dev/kmsg ]]; then
echo "<6>vmtest: $*" >/dev/kmsg
else
Expand Down Expand Up @@ -83,14 +83,14 @@ vport=
for dir in /sys/class/virtio-ports/*; do
if [[ "$(cat "$dir/name")" == "org.qemu.guest_agent.0" ]]; then
vport_name=$(basename "$dir")
vport="/dev/${vport_name}"
vport="/dev/$\{vport_name}"
fi
done
if [[ -z "$vport" ]]; then
log "Failed to locate qemu-guest-agent virtio-port"
exit 1
fi
log "Located qemu-guest-agent virtio port: ${vport}"
log "Located qemu-guest-agent virtio port: $\{vport}"

# Send QGA logs out via kmsg if possible
qga_logs=
Expand Down
52 changes: 40 additions & 12 deletions src/qemu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::output::Output;
use crate::qga::QgaWrapper;
use crate::{Mount, Target, VMConfig};

const INIT_SCRIPT: &str = include_str!("init/init.sh");
const INIT_TEMPLATE: &str = include_str!("init/init.sh.template");
const COMMAND_TEMPLATE: &str = include_str!("init/command.template");
// Needs to be `/dev/root` for kernel to "find" the 9pfs as rootfs
const ROOTFS_9P_FS_MOUNT_TAG: &str = "/dev/root";
Expand Down Expand Up @@ -87,8 +87,32 @@ struct CommandContext {
command_output_port_name: String,
}

/// Used by templating engine to render init.sh
#[derive(Serialize)]
struct InitContext {
/// $PATH the guest should use
path: String,
}

const QEMU_DEFAULT_ARGS: &[&str] = &["-nodefaults", "-display", "none"];

/// Gets instance of template engine
///
/// Unfortunately cannot be done as a lazy_static or OnceCell b/c TinyTemplate
/// internally uses some !Sync stuff.
fn get_templates() -> TinyTemplate<'static> {
// Disable HTML escaping (b/c we're not dealing with HTML)
let mut tt = TinyTemplate::new();
tt.set_default_formatter(&format_unescaped);

// We are ok panicing here b/c there should never be a runtime
// error compiling the template. Any errors are trivial bugs.
tt.add_template("cmd", COMMAND_TEMPLATE).unwrap();
tt.add_template("init", INIT_TEMPLATE).unwrap();

tt
}

/// Whether or not the host supports KVM
fn host_supports_kvm(arch: &str) -> bool {
arch == ARCH && Path::new("/dev/kvm").exists()
Expand Down Expand Up @@ -134,6 +158,18 @@ fn guest_init_path(guest_temp_dir: PathBuf, host_init_path: PathBuf) -> Result<P
Ok(guest_init_path)
}

/// Generates init.sh that guest will use as pid 1
fn init_script() -> String {
let path = match env::var("PATH") {
Ok(p) => p,
Err(_) => "/bin:/sbin:/usr/bin:/usr/sbin".to_string(),
};

// Ignore errors cuz only trivial bugs are possible
let context = InitContext { path };
get_templates().render("init", &context).unwrap()
}

// Given a rootfs, generate a tempfile with the init script inside.
// Returns the tempfile and the path to the init script inside the guest.
// When rootfs is /, both the tempfile filename and guest init path are equal.
Expand All @@ -152,7 +188,7 @@ fn gen_init(rootfs: &Path) -> Result<(NamedTempFile, PathBuf)> {
.context("Failed to create tempfile")?;

host_init
.write_all(INIT_SCRIPT.as_bytes())
.write_all(init_script().as_bytes())
.context("Failed to write init to tmpfs")?;

// Set write bits on script
Expand Down Expand Up @@ -743,14 +779,6 @@ impl Qemu {

/// Generates a bash script that runs `self.command`
fn command_script(&self) -> String {
// Disable HTML escaping (b/c we're not dealing with HTML)
let mut tt = TinyTemplate::new();
tt.set_default_formatter(&format_unescaped);

// We are ok panicing here b/c there should never be a runtime
// error compiling the template. Any errors are trivial bugs.
tt.add_template("cmd", COMMAND_TEMPLATE).unwrap();

let context = CommandContext {
// Only `cd` for kernel targets that share userspace with host
should_cd: !self.image && self.rootfs == Target::default_rootfs(),
Expand All @@ -759,8 +787,8 @@ impl Qemu {
command_output_port_name: COMMAND_OUTPUT_PORT_NAME.into(),
};

// Same as above, ignore errors cuz only trivial bugs are possible
tt.render("cmd", &context).unwrap()
// Ignore errors cuz only trivial bugs are possible
get_templates().render("cmd", &context).unwrap()
}

/// Run this target's command inside the VM
Expand Down

0 comments on commit 468fd04

Please sign in to comment.