add process handling mechanisms

This commit is contained in:
Ryan Heywood 2025-07-09 00:50:01 -04:00
parent ac43f98cf6
commit d9b4c98c1b
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 130 additions and 15 deletions

43
src/config.rs Normal file
View File

@ -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<Box<dyn Platform>>,
pub mode: Mode,
pub target: String,
}
pub fn get_config() -> Result<Config> {
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 })
}

View File

@ -1,6 +1,7 @@
mod config;
mod platform;
mod result; mod result;
mod system; mod system;
mod platform;
use result::Result; 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<()> { fn init() -> Result<()> {
platform::init()?; let config = config::get_config()?;
println!("Hello, world!");
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(()) Ok(())
} }

View File

@ -1,6 +1,6 @@
use crate::{system::{self, Mount, MountType}, result::Result}; use crate::{system::{self, Mount, MountType}, result::Result};
trait Platform { pub trait Platform {
/// Whether the current Platform is the `Self` platform. /// Whether the current Platform is the `Self` platform.
/// ///
/// This method should check for the existence of hardware devices that irrefutably defines /// This method should check for the existence of hardware devices that irrefutably defines
@ -61,7 +61,16 @@ fn init_modules(iter: impl IntoIterator<Item = (String, String)>) -> Result<()>
#[cfg(feature = "aws")] #[cfg(feature = "aws")]
mod aws; mod aws;
fn get_current_platform() -> Result<Option<Box<dyn Platform>>> { pub fn get_current_platform(name: Option<&str>) -> Result<Option<Box<dyn Platform>>> {
#[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")] #[cfg(feature = "aws")]
if aws::Aws.is()? { if aws::Aws.is()? {
return Ok(Some(Box::new(aws::Aws))) return Ok(Some(Box::new(aws::Aws)))
@ -70,18 +79,15 @@ fn get_current_platform() -> Result<Option<Box<dyn Platform>>> {
Ok(None) 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 // Error handling strategy: If a platform is compiled in and loaded, if platform
// specific error handling doesn't work, fall back to generic. // 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. // NOTE: We need to make get_mounts _additional_ beyond a base set.
// We need `/dev/nsm` to exist so Aws.is() works. // We need `/dev/nsm` to exist so Aws.is() works.
platform.get_mounts().and_then(init_filesystems)?; platform.get_mounts().and_then(init_filesystems)?;
platform.get_modules().and_then(init_modules)?; platform.get_modules().and_then(init_modules)?;
platform.init()?; platform.init()?;
}
Ok(()) Ok(())
} }

View File

@ -8,7 +8,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use crate::result::{ctx_os_error, Context, Result}; 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::{ use std::{
ffi::CString, ffi::CString,
os::fd::{AsRawFd, RawFd}, os::fd::{AsRawFd, RawFd},
@ -165,3 +165,39 @@ pub fn close(fd: RawFd) -> Result<()> {
n => unreachable!("close({fd}) returned bad value: {n}"), n => unreachable!("close({fd}) returned bad value: {n}"),
} }
} }
pub fn execv(command: impl AsRef<Path>, 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<libc::pid_t> {
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}"),
}
}