Files
RedBear-OS/init/src/main.rs
T
2025-11-01 19:29:52 -06:00

227 lines
8.9 KiB
Rust

use std::collections::BTreeMap;
use std::env;
use std::ffi::CString;
use std::fs::{read_dir, File};
use std::io::{BufRead, BufReader, Result};
use std::path::Path;
use std::process::Command;
use libredox::error::Error as OsError;
use libredox::flag::{O_RDONLY, O_WRONLY};
fn set_default_scheme(scheme: &str) -> std::result::Result<(), OsError> {
use std::ffi::{c_char, c_int};
extern "C" {
fn set_default_scheme(scheme: *const c_char) -> c_int;
}
let cstr =
CString::new(scheme.as_bytes()).expect(&format!("init: invalid default scheme {}", scheme));
let res = unsafe { set_default_scheme(cstr.as_ptr()) };
match res {
0 => Ok(()),
error_code => Err(OsError::new(error_code)),
}
}
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(())
}
pub fn run(file: &Path) -> Result<()> {
let file = File::open(file)?;
let reader = BufReader::new(file);
for line_res in reader.lines() {
let line_raw = line_res?;
let line = line_raw.trim();
if !line.is_empty() && !line.starts_with('#') {
let mut args = line.split(' ').map(|arg| {
if arg.starts_with('$') {
env::var(&arg[1..]).unwrap_or(String::new())
} else {
arg.to_string()
}
});
if let Some(cmd) = args.next() {
match cmd.as_str() {
"cd" => {
if let Some(dir) = args.next() {
if let Err(err) = env::set_current_dir(&dir) {
println!("init: failed to cd to '{}': {}", dir, err);
}
} else {
println!("init: failed to cd: no argument");
}
}
"set-default-scheme" => {
if let Some(scheme) = args.next() {
if let Err(err) = set_default_scheme(&scheme) {
println!(
"init: failed to set default scheme to '{}': {}",
scheme, err
);
}
} else {
println!("init: failed to set default scheme: no argument");
}
}
"echo" => {
if let Some(arg) = args.next() {
print!("{}", arg);
}
for arg in args {
print!(" {}", arg);
}
print!("\n");
}
"export" => {
if let Some(var) = args.next() {
let mut value = String::new();
if let Some(arg) = args.next() {
value.push_str(&arg);
}
for arg in args {
value.push(' ');
value.push_str(&arg);
}
env::set_var(var, value);
} else {
println!("init: failed to export: no argument");
}
}
"run" => {
if let Some(new_file) = args.next() {
if let Err(err) = run(&Path::new(&new_file)) {
println!("init: failed to run '{}': {}", new_file, err);
}
} else {
println!("init: failed to run: no argument");
}
}
"run.d" => {
// This must be a BTreeMap to iterate in sorted order.
let mut entries = BTreeMap::new();
let mut missing_arg = true;
for new_dir in args {
if !Path::new(&new_dir).exists() {
// Skip non-existent dirs
continue;
}
missing_arg = false;
match read_dir(&new_dir) {
Ok(list) => {
for entry_res in list {
match entry_res {
Ok(entry) => {
// This intentionally overwrites older entries with
// the same filename to allow overriding entries in
// one search dir with those in a later search dir.
entries.insert(entry.file_name(), entry.path());
}
Err(err) => {
println!(
"init: failed to run.d: '{}': {}",
new_dir, err
);
}
}
}
}
Err(err) => {
println!("init: failed to run.d: '{}': {}", new_dir, err);
}
}
}
if missing_arg {
println!(
"init: failed to run.d: no argument or all dirs are non-existent"
);
} else {
// This takes advantage of BTreeMap iterating in sorted order.
for (_, entry_path) in entries {
if let Err(err) = run(&entry_path) {
println!(
"init: failed to run '{}': {}",
entry_path.display(),
err
);
}
}
}
}
"stdio" => {
if let Some(stdio) = args.next() {
if let Err(err) = switch_stdio(&stdio) {
println!("init: failed to switch stdio to '{}': {}", stdio, err);
}
} else {
println!("init: failed to set stdio: no argument");
}
}
"unset" => {
for arg in args {
env::remove_var(&arg);
}
}
_ => {
let mut command = Command::new(cmd.clone());
for arg in args {
command.arg(arg);
}
match command.spawn() {
Ok(mut child) => match child.wait() {
Ok(exit_status) => {
if !exit_status.success() {
println!("{cmd} failed with {exit_status}");
}
}
Err(err) => {
println!("init: failed to wait for '{}': {}", line, err)
}
},
Err(err) => println!("init: failed to execute '{}': {}", line, err),
}
}
}
}
}
}
Ok(())
}
pub fn main() {
if let Err(err) = set_default_scheme("initfs") {
println!("init: failed to set default scheme: {}", err);
}
let config = "/etc/init.rc";
if let Err(err) = run(&Path::new(config)) {
println!("init: failed to run {}: {}", config, err);
}
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();
}
}