diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..85cdc67 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,43 @@ +use crate::platform::{self, Platform}; +use crate::result::{Context, Result}; + +pub enum Mode { + Spawn, + Exec, +} + +pub struct Config { + pub platform: Option>, + pub mode: Mode, + pub target: String, +} + +pub fn get_config() -> Result { + let mut values = std::collections::HashMap::<&str, String>::new(); + let cmdline = std::fs::read_to_string("/proc/cmdline") + .context(format_args!("could not read kernel cmdline"))?; + + for word in cmdline.split_whitespace() { + if let Some((lhs, rhs)) = word.split_once('=') { + if let Some(("nit", name)) = lhs.split_once('.') { + values.insert(name, rhs.to_string()); + } + } + } + + let mode = if let Some(mode) = values.remove("mode") { + match &*mode { + "spawn" => Mode::Spawn, + "exec" => Mode::Exec, + m => panic!("Bad mode: {m}"), + } + } else { + Mode::Spawn + }; + + let platform = platform::get_current_platform(values.remove("platform").as_deref())?; + + let target = values.remove("target").unwrap(); + + Ok(Config { platform, mode, target }) +} diff --git a/src/main.rs b/src/main.rs index 619ab62..d0fdd2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ +mod config; +mod platform; mod result; mod system; -mod platform; use result::Result; @@ -15,9 +16,38 @@ fn main() { } } +extern "C" fn handle_sigchld(_sig: i32) { + // wait on all dead processes + while system::syscall::waitpid(-1, libc::WNOHANG).is_ok() { + // pass + } +} + fn init() -> Result<()> { - platform::init()?; - println!("Hello, world!"); + let config = config::get_config()?; + + if let Some(platform) = config.platform.as_deref() { + platform::init(platform)?; + } else if let Some(platform) = platform::get_current_platform(None)?.as_deref() { + platform::init(platform)?; + } + + let command = &config.target; + match config.mode { + config::Mode::Spawn => { + // set up a process reaper. any time a child process dies, a SIGCHLD will be fired, and + // the signal handler will reap the processes + system::syscall::signal(libc::SIGCHLD, handle_sigchld)?; + loop { + if let Err(e) = std::process::Command::new(command).spawn() { + eprintln!("Encountered error running {command}: {e}"); + } + } + } + config::Mode::Exec => { + system::syscall::execv(command, &[])?; + } + } Ok(()) } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 51a1261..dd68202 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,6 +1,6 @@ use crate::{system::{self, Mount, MountType}, result::Result}; -trait Platform { +pub trait Platform { /// Whether the current Platform is the `Self` platform. /// /// This method should check for the existence of hardware devices that irrefutably defines @@ -61,7 +61,16 @@ fn init_modules(iter: impl IntoIterator) -> Result<()> #[cfg(feature = "aws")] mod aws; -fn get_current_platform() -> Result>> { +pub fn get_current_platform(name: Option<&str>) -> Result>> { + #[allow(clippy::collapsible_match)] + if let Some(name) = name { + match name { + #[cfg(feature = "aws")] + "aws" => return Ok(Some(Box::new(aws::Aws))), + _ => {} + } + } + #[cfg(feature = "aws")] if aws::Aws.is()? { return Ok(Some(Box::new(aws::Aws))) @@ -70,18 +79,15 @@ fn get_current_platform() -> Result>> { Ok(None) } -pub fn init() -> Result<()> { +pub fn init(platform: &dyn Platform) -> Result<()> { // Error handling strategy: If a platform is compiled in and loaded, if platform // specific error handling doesn't work, fall back to generic. - let platform = get_current_platform()?; - if let Some(platform) = platform { - // NOTE: We need to make get_mounts _additional_ beyond a base set. - // We need `/dev/nsm` to exist so Aws.is() works. - platform.get_mounts().and_then(init_filesystems)?; - platform.get_modules().and_then(init_modules)?; - platform.init()?; - } + // NOTE: We need to make get_mounts _additional_ beyond a base set. + // We need `/dev/nsm` to exist so Aws.is() works. + platform.get_mounts().and_then(init_filesystems)?; + platform.get_modules().and_then(init_modules)?; + platform.init()?; Ok(()) } diff --git a/src/system/syscall.rs b/src/system/syscall.rs index 496aa45..c960705 100644 --- a/src/system/syscall.rs +++ b/src/system/syscall.rs @@ -8,7 +8,7 @@ #![allow(dead_code)] use crate::result::{ctx_os_error, Context, Result}; -use libc::{self, c_ulong, c_void}; +use libc::{self, c_int, c_ulong, c_void}; use std::{ ffi::CString, os::fd::{AsRawFd, RawFd}, @@ -165,3 +165,39 @@ pub fn close(fd: RawFd) -> Result<()> { n => unreachable!("close({fd}) returned bad value: {n}"), } } + +pub fn execv(command: impl AsRef, args: &[&str]) -> Result<()> { + let command = command.as_ref(); + + let command_cstr = CString::new(command.as_os_str().as_encoded_bytes()) + .context(format_args!("bad command: {}", command.display()))?; + let mut args_cstr = vec![]; + for arg in args { + let cur_arg_cstr = CString::new(arg.as_bytes()).context(format_args!("bad arg: {arg}")); + args_cstr.push(cur_arg_cstr); + } + + if unsafe { libc::execv(command_cstr.as_ptr().cast(), args_cstr.as_ptr().cast()) } == -1 { + return ctx_os_error(format_args!("error calling exec()")); + } + + Ok(()) +} + +pub type SignalHandler = extern "C" fn(c_int); + +pub fn signal(signal: i32, handler: SignalHandler) -> Result<()> { + #[allow(clippy::single_match)] + match unsafe { libc::signal(signal as i32, handler as libc::sighandler_t) } { + libc::SIG_ERR => ctx_os_error(format_args!("invalid handler for {signal:?}")), + _ => Ok(()), + } +} + +pub fn waitpid(pid: libc::pid_t, flags: i32) -> Result { + match unsafe { libc::waitpid(pid, std::ptr::null_mut(), flags) } { + pid @ 0.. => Ok(pid), + -1 => ctx_os_error(format_args!("error calling waitpid({pid}, {flags})")).map(|()| 0), + n => unreachable!("waitpid({pid}, {flags}) returned bad value: {n}"), + } +}