hypha: init will now watch process in background

This commit is contained in:
Alex Zenla 2024-01-31 01:40:42 -08:00
parent b6af5f54bd
commit 86c512474a
No known key found for this signature in database
GPG Key ID: 067B238899B51269
7 changed files with 77 additions and 20 deletions

View File

@ -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(())

View File

@ -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();

View File

@ -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<CString>,
env: Vec<CString>,
) -> 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));
}
}
}

View File

@ -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}"

View File

@ -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,

View File

@ -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);

View File

@ -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<T> = std::result::Result<T, Error>;