From 00b5957d6e4849d397d1c398e8ebd7f2c29572db Mon Sep 17 00:00:00 2001 From: ryan Date: Sun, 10 Aug 2025 22:17:56 -0400 Subject: [PATCH] add environment variable configuration --- crates/nit/src/config.rs | 43 ++++++++++++++++++++++++++++++-- crates/nit/src/main.rs | 4 ++- crates/nit/src/system/syscall.rs | 18 +++++++++++-- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/crates/nit/src/config.rs b/crates/nit/src/config.rs index f520087..6bacc94 100644 --- a/crates/nit/src/config.rs +++ b/crates/nit/src/config.rs @@ -1,3 +1,8 @@ +use std::{ + io::{BufRead, BufReader}, + path::PathBuf, +}; + use crate::platform::{self, Platform}; use crate::result::{Context, Result}; @@ -12,6 +17,31 @@ pub struct Config { pub platform: Option>, pub mode: Mode, pub target: String, + pub environment: Vec, +} + +pub fn get_environment() -> Result> { + let mut environment = vec![]; + let environment_file = PathBuf::from("/etc/environment"); + + // NOTE: We should never have to care about permissions. Therefore, we don't use .try_exists() + // As for TOCTOU... we're init, and this is the first code to run. + if environment_file.exists() { + let file = std::fs::File::open(&environment_file).context(format_args!( + "could not open: {environment_file}", + environment_file = environment_file.display(), + ))?; + let reader = BufReader::new(file); + for line in reader.lines() { + let line = line.context(format_args!( + "could not read line from: {environment_file}", + environment_file = environment_file.display() + ))?; + environment.push(line); + } + } + + Ok(environment) } pub fn get_config() -> Result { @@ -39,7 +69,16 @@ pub fn get_config() -> Result { let platform = platform::get_current_platform(values.remove("platform").as_deref())?; - let target = values.remove("target").unwrap_or(String::from("/usr/bin/hello")); + let target = values + .remove("target") + .unwrap_or(String::from("/usr/bin/hello")); - Ok(Config { platform, mode, target }) + let environment = get_environment()?; + + Ok(Config { + platform, + mode, + target, + environment, + }) } diff --git a/crates/nit/src/main.rs b/crates/nit/src/main.rs index c282fef..b3bc69c 100644 --- a/crates/nit/src/main.rs +++ b/crates/nit/src/main.rs @@ -17,6 +17,7 @@ fn main() { } } +#[allow(dead_code)] extern "C" fn handle_sigchld(_sig: i32) { // wait on all dead processes while system::syscall::waitpid(-1, libc::WNOHANG).is_ok() { @@ -66,7 +67,8 @@ fn init() -> Result<()> { } config::Mode::Exec => { dmesg(format!("pivoting to {command}")); - system::syscall::execv(command, &[])?; + let envs = config.environment.iter().map(|e| &**e).collect::>(); + system::syscall::execve(command, &[], &envs)?; } } diff --git a/crates/nit/src/system/syscall.rs b/crates/nit/src/system/syscall.rs index 72dc32d..461d90d 100644 --- a/crates/nit/src/system/syscall.rs +++ b/crates/nit/src/system/syscall.rs @@ -193,7 +193,7 @@ pub fn close(fd: RawFd) -> Result<()> { } } -pub fn execv(command: impl AsRef, args: &[&str]) -> Result<()> { +pub fn execve(command: impl AsRef, args: &[&str], envs: &[&str]) -> Result<()> { let command = command.as_ref(); let command_cstr = CString::new(command.as_os_str().as_encoded_bytes()) @@ -213,7 +213,21 @@ pub fn execv(command: impl AsRef, args: &[&str]) -> Result<()> { .chain(std::iter::once(std::ptr::null())) .collect(); - if unsafe { libc::execv(command_cstr.as_ptr().cast(), args_ptrs.as_ptr().cast()) } == -1 { + let mut envs_cstr = vec![command_cstr.clone()]; + for env in envs { + let cur_env_cstr = CString::new(env.as_bytes()).context(format_args!("bad env: {env}"))?; + envs_cstr.push(cur_env_cstr); + } + + // NOTE: The last env must be a null pointer, but we can't construct a CString from nullptr, so + // we construct an array of pointers and use that + let envs_ptrs: Vec<_> = envs_cstr + .iter() + .map(|s| s.as_ptr()) + .chain(std::iter::once(std::ptr::null())) + .collect(); + + if unsafe { libc::execve(command_cstr.as_ptr().cast(), args_ptrs.as_ptr().cast(), envs_ptrs.as_ptr().cast()) } == -1 { return ctx_os_error(format_args!( "error calling exec({command}, {args:?})", command = command.display()