227 lines
8.9 KiB
Rust
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();
|
|
}
|
|
}
|