Files
RedBear-OS/init/src/main.rs
T
bjorn3 73cc0b0a07 init: Begin introducing units
Dependencies are not yet functional.
2026-02-25 20:57:47 +01:00

146 lines
3.8 KiB
Rust

use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::io::Result;
use std::path::Path;
use libredox::flag::{O_RDONLY, O_WRONLY};
use crate::script::Command;
use crate::unit::Unit;
mod script;
mod service;
mod unit;
fn switch_stdio(stdio: &str) -> Result<()> {
let stdin = libredox::Fd::open(stdio, O_RDONLY, 0)?;
let stdout = libredox::Fd::open(stdio, O_WRONLY, 0)?;
let stderr = libredox::Fd::open(stdio, O_WRONLY, 0)?;
stdin.dup2(0, &[])?;
stdout.dup2(1, &[])?;
stderr.dup2(2, &[])?;
Ok(())
}
struct InitConfig {
log_debug: bool,
skip_cmd: Vec<String>,
envs: BTreeMap<String, OsString>,
}
impl InitConfig {
fn new() -> Self {
let log_level = env::var("INIT_LOG_LEVEL").unwrap_or("INFO".into());
let log_debug = matches!(log_level.as_str(), "DEBUG" | "TRACE");
let skip_cmd: Vec<String> = match env::var("INIT_SKIP") {
Ok(v) if v.len() > 0 => v.split(',').map(|s| s.to_string()).collect(),
_ => Vec::new(),
};
Self {
log_debug,
skip_cmd,
envs: BTreeMap::from([("RUST_BACKTRACE".to_owned(), "1".into())]),
}
}
}
fn switch_root(prefix: &Path, etcdir: &Path, config: &mut InitConfig) {
config
.envs
.insert("PATH".to_owned(), prefix.join("bin").into_os_string());
config.envs.insert(
"LD_LIBRARY_PATH".to_owned(),
prefix.join("lib").into_os_string(),
);
let entries = match config::config_for_dirs(&[
prefix.join("lib").join("init.d"),
etcdir.join("init.d"),
]) {
Ok(list) => list,
Err(err) => {
eprintln!("init: failed to switchroot: '{prefix:?}', '{etcdir:?}': {err}");
return;
}
};
for entry_path in entries {
if let Err(err) = run(&entry_path, config) {
eprintln!("init: failed to run '{}': {}", entry_path.display(), err);
}
}
}
fn run(file: &Path, config: &mut InitConfig) -> Result<()> {
let (unit, errors) = Unit::from_file(file)?;
for error in errors {
eprintln!("init: {}: {error}", file.display());
}
match unit.kind {
unit::UnitKind::LegacyScript { script } => {
for cmd in script.0 {
if config.log_debug {
eprintln!("init: running: {cmd:?}");
}
run_command(cmd, config);
}
}
unit::UnitKind::Service { service } => {
if config.log_debug {
eprintln!("init: running: {} {}", service.cmd, service.args.join(" "));
}
service.spawn(&config.envs);
}
}
Ok(())
}
fn run_command(cmd: Command, config: &mut InitConfig) {
match cmd {
Command::Nothing => {}
Command::Echo(text) => println!("{text}"),
Command::SwitchRoot(prefix, etcdir) => {
switch_root(&prefix, &etcdir, config);
}
Command::Stdio(stdio) => {
if let Err(err) = switch_stdio(&stdio) {
eprintln!("init: failed to switch stdio to '{}': {}", stdio, err);
}
}
Command::Service(service) => {
if config.skip_cmd.contains(&service.cmd) {
eprintln!(
"init: skipping '{} {}'",
service.cmd,
service.args.join(" ")
);
return;
}
service.spawn(&config.envs);
}
}
}
fn main() {
let mut init_config = InitConfig::new();
switch_root(
Path::new("/scheme/initfs"),
Path::new("/scheme/initfs/etc"),
&mut init_config,
);
libredox::call::setrens(0, 0).expect("init: failed to enter null namespace");
loop {
let mut status = 0;
libredox::call::waitpid(0, &mut status, 0).unwrap();
}
}