diff --git a/hypha/bin/container.rs b/hypha/bin/container.rs index f2114d4..6fa5a1d 100644 --- a/hypha/bin/container.rs +++ b/hypha/bin/container.rs @@ -1,10 +1,20 @@ -use std::env; -use anyhow::Result; +use anyhow::{anyhow, Result}; use hypha::container::init::ContainerInit; +use std::env; fn main() -> Result<()> { env::set_var("RUST_BACKTRACE", "1"); - env_logger::init(); + if env::var("HYPHA_UNSAFE_ALWAYS_ALLOW_INIT").unwrap_or("0".to_string()) != "1" { + let pid = std::process::id(); + if pid > 3 { + return Err(anyhow!( + "not running because the pid of {} indicates this is probably not \ + the right context for the init daemon. \ + run with HYPHA_UNSAFE_ALWAYS_ALLOW_INIT=1 to bypass this check", + pid + )); + } + } let mut container = ContainerInit::new(); container.init()?; Ok(()) diff --git a/hypha/bin/controller.rs b/hypha/bin/controller.rs index c1c369f..f0b70c4 100644 --- a/hypha/bin/controller.rs +++ b/hypha/bin/controller.rs @@ -1,4 +1,3 @@ -use std::env; use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; use hypha::ctl::Controller; @@ -47,7 +46,6 @@ enum Commands { } fn main() -> Result<()> { - env::set_var("RUST_BACKTRACE", "1"); env_logger::init(); let args = ControllerArgs::parse(); diff --git a/hypha/src/container/init.rs b/hypha/src/container/init.rs index 73de850..951d23c 100644 --- a/hypha/src/container/init.rs +++ b/hypha/src/container/init.rs @@ -1,16 +1,19 @@ use crate::shared::LaunchInfo; use anyhow::{anyhow, Result}; use log::trace; -use nix::libc::dup2; -use nix::unistd::execve; +use nix::libc::{c_int, dup2, wait}; +use nix::unistd::{execve, fork, ForkResult, Pid}; use oci_spec::image::{Config, ImageConfiguration}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::fs; use std::fs::{File, OpenOptions, Permissions}; use std::os::fd::AsRawFd; use std::os::linux::fs::MetadataExt; use std::os::unix::fs::{chroot, PermissionsExt}; use std::path::Path; +use std::ptr::addr_of_mut; +use std::thread::sleep; +use std::time::Duration; use sys_mount::{FilesystemType, Mount, MountFlags}; use walkdir::WalkDir; @@ -65,9 +68,8 @@ impl ContainerInit { self.mount_new_root()?; self.nuke_initrd()?; self.bind_new_root()?; - self.map_console(console)?; if let Some(cfg) = config.config() { - self.run(cfg, &launch)?; + self.run(console, cfg, &launch)?; } else { return Err(anyhow!( "unable to determine what to execute, image config doesn't tell us" @@ -254,7 +256,7 @@ impl ContainerInit { } fn map_console(&mut self, console: File) -> Result<()> { - trace!("map console"); + trace!("mapping console"); unsafe { dup2(console.as_raw_fd(), 0); dup2(console.as_raw_fd(), 1); @@ -264,7 +266,7 @@ impl ContainerInit { Ok(()) } - fn run(&mut self, config: &Config, launch: &LaunchInfo) -> Result<()> { + fn run(&mut self, console: File, config: &Config, launch: &LaunchInfo) -> Result<()> { let mut cmd = match config.cmd() { None => vec![], Some(value) => value.clone(), @@ -278,7 +280,6 @@ impl ContainerInit { cmd.push("/bin/sh".to_string()); } - trace!("running container command: {}", cmd.join(" ")); let path = cmd.remove(0); let mut env = match config.env() { None => vec![], @@ -288,6 +289,9 @@ impl ContainerInit { if let Some(extra_env) = &launch.env { env.extend_from_slice(extra_env.as_slice()); } + + trace!("running container command: {}", cmd.join(" ")); + let path_cstr = CString::new(path)?; let cmd_cstr = ContainerInit::strings_as_cstrings(cmd)?; let env_cstr = ContainerInit::strings_as_cstrings(env)?; @@ -302,7 +306,7 @@ impl ContainerInit { } std::env::set_current_dir(&working_dir)?; - execve(&path_cstr, &cmd_cstr, &env_cstr)?; + self.fork_and_exec(console, &path_cstr, cmd_cstr, env_cstr)?; Ok(()) } @@ -313,4 +317,39 @@ impl ContainerInit { } Ok(results) } + + fn fork_and_exec( + &mut self, + console: File, + path: &CStr, + cmd: Vec, + env: Vec, + ) -> Result<()> { + match unsafe { fork()? } { + ForkResult::Parent { child } => self.background(child), + ForkResult::Child => { + self.map_console(console)?; + execve(path, &cmd, &env)?; + Ok(()) + } + } + } + + fn background(&mut self, executed: Pid) -> Result<()> { + loop { + let mut status: c_int = 0; + let pid = unsafe { wait(addr_of_mut!(status)) }; + if executed.as_raw() == pid { + return self.death(status); + } + } + } + + fn death(&mut self, code: c_int) -> Result<()> { + println!("[hypha] container process exited: status = {}", code); + println!("[hypha] looping forever"); + loop { + sleep(Duration::from_secs(1)); + } + } } diff --git a/initrd/build.sh b/initrd/build.sh index 6f82fc8..5282aa6 100755 --- a/initrd/build.sh +++ b/initrd/build.sh @@ -7,7 +7,8 @@ HYPHA_DIR="${PWD}" cargo build --release --target x86_64-unknown-linux-gnu INITRD_DIR="$(mktemp -d /tmp/hypha-initrd.XXXXXXXXXXXXX)" cp target/x86_64-unknown-linux-gnu/release/hyphactr "${INITRD_DIR}/init" +chmod +x "${INITRD_DIR}/init" cd "${INITRD_DIR}" mkdir -p "${HYPHA_DIR}/target/initrd" -find . | cpio -o -H newc --quiet > "${HYPHA_DIR}/target/initrd/initrd" +find . | cpio -R 0:0 --reproducible -o -H newc --quiet > "${HYPHA_DIR}/target/initrd/initrd" rm -rf "${INITRD_DIR}" diff --git a/xen/xenclient/src/elfloader.rs b/xen/xenclient/src/elfloader.rs index c9841d9..1bca930 100644 --- a/xen/xenclient/src/elfloader.rs +++ b/xen/xenclient/src/elfloader.rs @@ -273,8 +273,8 @@ impl BootImageLoader for ElfImageLoader { copy_slice.len() ); copy(segment_dst, copy_slice); - if memsz - filesz > 0 { - let remaining = &mut segment_dst[filesz as usize..(memsz - filesz) as usize]; + if (memsz - filesz) > 0 { + let remaining = &mut segment_dst[filesz as usize..memsz as usize]; debug!( "ElfImageLoader load fill_zero hdr={:?} dst={:#x} len={}", header.p_offset, diff --git a/xen/xenstore/src/client.rs b/xen/xenstore/src/client.rs index 745107b..cbb90a4 100644 --- a/xen/xenstore/src/client.rs +++ b/xen/xenstore/src/client.rs @@ -55,7 +55,7 @@ pub trait XsdInterface { Ok(match self.read_string(path) { Ok(value) => Some(value), Err(error) => { - if error.to_string() == "ENOENT" { + if error.is_noent_response() { None } else { return Err(error); @@ -68,7 +68,7 @@ pub trait XsdInterface { Ok(match self.list(path) { Ok(value) => value, Err(error) => { - if error.to_string() == "ENOENT" { + if error.is_noent_response() { Vec::new() } else { return Err(error); @@ -115,7 +115,7 @@ impl XsdClient { trace!("rm tx={tx} path={path}"); let result = self.socket.send_single(tx, XSD_RM, path); if let Err(error) = result { - if error.to_string() == "ENOENT" { + if error.is_noent_response() { return Ok(true); } return Err(error); diff --git a/xen/xenstore/src/error.rs b/xen/xenstore/src/error.rs index 50f12ef..14ae279 100644 --- a/xen/xenstore/src/error.rs +++ b/xen/xenstore/src/error.rs @@ -28,4 +28,13 @@ pub enum Error { InvalidPermissions, } +impl Error { + pub fn is_noent_response(&self) -> bool { + match self { + Error::ResponseError(message) => message == "ENOENT", + _ => false, + } + } +} + pub type Result = std::result::Result;