Merge branch 'init_rework2' into 'main'
Reduce global state manipulation in init See merge request redox-os/base!130
This commit is contained in:
@@ -100,6 +100,7 @@ pub fn main() -> ! {
|
||||
.filter(|var| !var.starts_with(b"INITFS_"))
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
envs.push(b"RUST_BACKTRACE=1");
|
||||
//envs.push(b"LD_DEBUG=all");
|
||||
envs.push(b"LD_LIBRARY_PATH=/scheme/initfs/lib");
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ extern crate syscall;
|
||||
use driver_graphics::GraphicsScheme;
|
||||
use event::{user_data, EventQueue};
|
||||
use inputd::DisplayHandle;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
@@ -57,9 +58,17 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
let mut framebuffers = vec![unsafe { FrameBuffer::new(phys, width, height, stride) }];
|
||||
|
||||
//TODO: ideal maximum number of outputs?
|
||||
let bootloader_env = std::fs::read_to_string("/scheme/sys/env")
|
||||
.expect("failed to read env")
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let (env, value) = line.split_once('=').unwrap();
|
||||
(env.to_owned(), value.to_owned())
|
||||
})
|
||||
.collect::<HashMap<String, String>>();
|
||||
for i in 1..1024 {
|
||||
match env::var(&format!("FRAMEBUFFER{}", i)) {
|
||||
Ok(var) => match unsafe { FrameBuffer::parse(&var) } {
|
||||
match bootloader_env.get(&format!("FRAMEBUFFER{}", i)) {
|
||||
Some(var) => match unsafe { FrameBuffer::parse(&var) } {
|
||||
Some(fb) => {
|
||||
println!(
|
||||
"vesad: framebuffer {}: {}x{} stride {} at 0x{:X}",
|
||||
@@ -71,7 +80,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
eprintln!("vesad: framebuffer {}: failed to parse '{}'", i, var);
|
||||
}
|
||||
},
|
||||
Err(_err) => break,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
notify hwd
|
||||
unset RSDP_ADDR RSDP_SIZE
|
||||
notify RSDP_ADDR=$ RSDP_SIZE=$ hwd
|
||||
pcid-spawner --initfs
|
||||
|
||||
+1
-5
@@ -1,8 +1,4 @@
|
||||
# Various daemons that relibc needs to function as well as a bunch of env vars
|
||||
# that should be set for every program.
|
||||
export PATH /scheme/initfs/bin
|
||||
export LD_LIBRARY_PATH /scheme/initfs/lib
|
||||
export RUST_BACKTRACE 1
|
||||
# Various daemons that relibc needs to function.
|
||||
rtcd
|
||||
scheme null nulld
|
||||
scheme zero zerod
|
||||
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
scheme input inputd
|
||||
notify vesad
|
||||
unset FRAMEBUFFER_ADDR FRAMEBUFFER_VIRT FRAMEBUFFER_WIDTH FRAMEBUFFER_HEIGHT FRAMEBUFFER_STRIDE
|
||||
#TODO: unset FRAMEBUFFER1 and beyond?
|
||||
notify FRAMEBUFFER_ADDR=$ FRAMEBUFFER_VIRT=$ FRAMEBUFFER_WIDTH=$ FRAMEBUFFER_HEIGHT=$ FRAMEBUFFER_STRIDE=$ vesad
|
||||
scheme fbbootlog fbbootlogd
|
||||
# Activate framebuffer log VT, which disables kernel graphical debug
|
||||
inputd -A 1
|
||||
|
||||
+1
-2
@@ -1,4 +1,3 @@
|
||||
notify ps2d
|
||||
notify hwd
|
||||
unset RSDP_ADDR RSDP_SIZE
|
||||
notify RSDP_ADDR=$ RSDP_SIZE=$ hwd
|
||||
pcid-spawner --initfs
|
||||
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK
|
||||
unset REDOXFS_UUID REDOXFS_BLOCK REDOXFS_PASSWORD_ADDR REDOXFS_PASSWORD_SIZE
|
||||
REDOXFS_PASSWORD_ADDR=$ REDOXFS_PASSWORD_SIZE=$ redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
cd /
|
||||
export PATH /usr/bin
|
||||
export LD_LIBRARY_PATH /usr/lib
|
||||
run.d /usr/lib/init.d /etc/init.d
|
||||
switchroot /usr /etc
|
||||
|
||||
+62
-64
@@ -1,7 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::io::Result;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
use libredox::flag::{O_RDONLY, O_WRONLY};
|
||||
|
||||
@@ -24,6 +25,7 @@ fn switch_stdio(stdio: &str) -> Result<()> {
|
||||
struct InitConfig {
|
||||
log_debug: bool,
|
||||
skip_cmd: Vec<String>,
|
||||
envs: BTreeMap<String, OsString>,
|
||||
}
|
||||
|
||||
impl InitConfig {
|
||||
@@ -38,11 +40,39 @@ impl InitConfig {
|
||||
Self {
|
||||
log_debug,
|
||||
skip_cmd,
|
||||
envs: BTreeMap::from([("RUST_BACKTRACE".to_owned(), "1".into())]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(file: &Path, config: &InitConfig) -> Result<()> {
|
||||
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 (script, errors) = script::Script::from_file(file)?;
|
||||
|
||||
for error in errors {
|
||||
@@ -59,30 +89,13 @@ fn run(file: &Path, config: &InitConfig) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_command(cmd: Command, config: &InitConfig) {
|
||||
fn run_command(cmd: Command, config: &mut InitConfig) {
|
||||
match cmd {
|
||||
Command::Nothing => {}
|
||||
Command::Cd(dir) => {
|
||||
if let Err(err) = env::set_current_dir(&dir) {
|
||||
eprintln!("init: failed to cd to '{}': {}", dir.display(), err);
|
||||
}
|
||||
}
|
||||
Command::Echo(text) => println!("{text}"),
|
||||
Command::Export(var, value) => unsafe { env::set_var(var, value) },
|
||||
Command::RunD(dirs) => {
|
||||
let entries = match config::config_for_dirs(&dirs) {
|
||||
Ok(list) => list,
|
||||
Err(err) => {
|
||||
eprintln!("init: failed to run.d: '{dirs:?}': {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for entry_path in entries {
|
||||
if let Err(err) = run(&entry_path, config) {
|
||||
eprintln!("init: failed to run '{}': {}", entry_path.display(), err);
|
||||
}
|
||||
}
|
||||
Command::SwitchRoot(prefix, etcdir) => {
|
||||
switch_root(&prefix, &etcdir, config);
|
||||
}
|
||||
Command::Stdio(stdio) => {
|
||||
if let Err(err) = switch_stdio(&stdio) {
|
||||
@@ -94,70 +107,62 @@ fn run_command(cmd: Command, config: &InitConfig) {
|
||||
unsafe { env::remove_var(&env) };
|
||||
}
|
||||
}
|
||||
Command::Nowait(cmd, args) => {
|
||||
if config.skip_cmd.contains(&cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd, args.join(" "));
|
||||
Command::Nowait(cmd) => {
|
||||
if config.skip_cmd.contains(&cmd.cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" "));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = process::Command::new(cmd);
|
||||
|
||||
for arg in args {
|
||||
command.arg(arg);
|
||||
}
|
||||
let mut command = cmd.into_command(&config.envs);
|
||||
|
||||
match command.spawn() {
|
||||
Ok(_child) => {}
|
||||
Err(err) => eprintln!("init: failed to execute '{:?}': {}", command, err),
|
||||
}
|
||||
}
|
||||
Command::Notify(cmd, args) => {
|
||||
if config.skip_cmd.contains(&cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd, args.join(" "));
|
||||
Command::Notify(cmd) => {
|
||||
if config.skip_cmd.contains(&cmd.cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" "));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = process::Command::new(&cmd);
|
||||
for arg in args {
|
||||
command.arg(arg);
|
||||
}
|
||||
let command = cmd.into_command(&config.envs);
|
||||
|
||||
daemon::Daemon::spawn(command);
|
||||
}
|
||||
Command::Scheme(scheme, cmd, args) => {
|
||||
if config.skip_cmd.contains(&cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd, args.join(" "));
|
||||
Command::Scheme(scheme, cmd) => {
|
||||
if config.skip_cmd.contains(&cmd.cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" "));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = process::Command::new(&cmd);
|
||||
for arg in args {
|
||||
command.arg(arg);
|
||||
}
|
||||
let command = cmd.into_command(&config.envs);
|
||||
|
||||
daemon::SchemeDaemon::spawn(command, &scheme);
|
||||
}
|
||||
Command::Regular(cmd, args) => {
|
||||
let mut command = process::Command::new(cmd.clone());
|
||||
for arg in args {
|
||||
command.arg(arg);
|
||||
Command::Regular(cmd) => {
|
||||
if config.skip_cmd.contains(&cmd.cmd) {
|
||||
eprintln!("init: skipping '{} {}'", cmd.cmd, cmd.args.join(" "));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = cmd.into_command(&config.envs);
|
||||
|
||||
let mut child = match command.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(err) => {
|
||||
eprintln!("init: failed to execute '{:?}': {}", command, err);
|
||||
eprintln!("init: failed to execute {:?}: {}", command, err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match child.wait() {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
eprintln!("{cmd} failed with {exit_status}");
|
||||
eprintln!("{command:?} failed with {exit_status}");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("init: failed to wait for '{:?}': {}", command, err)
|
||||
eprintln!("init: failed to wait for {:?}: {}", command, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,19 +170,12 @@ fn run_command(cmd: Command, config: &InitConfig) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let init_config = InitConfig::new();
|
||||
let entries = match config::config_for_initfs("init") {
|
||||
Ok(entries) => entries,
|
||||
Err(err) => {
|
||||
eprintln!("init: failed to find config files: {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
for entry_path in entries {
|
||||
if let Err(err) = run(&entry_path, &init_config) {
|
||||
eprintln!("init: failed to run '{}': {}", entry_path.display(), err);
|
||||
}
|
||||
}
|
||||
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");
|
||||
|
||||
|
||||
+83
-50
@@ -1,5 +1,15 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fs, io};
|
||||
use std::{env, fs, io, iter, process};
|
||||
|
||||
fn subst_env<'a>(arg: &str) -> String {
|
||||
if arg.starts_with('$') {
|
||||
env::var(&arg[1..]).unwrap_or(String::new())
|
||||
} else {
|
||||
arg.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Script(pub Vec<Command>);
|
||||
|
||||
@@ -14,15 +24,9 @@ impl Script {
|
||||
continue;
|
||||
}
|
||||
|
||||
let args = line.split(' ').map(|arg| {
|
||||
if arg.starts_with('$') {
|
||||
env::var(&arg[1..]).unwrap_or(String::new())
|
||||
} else {
|
||||
arg.to_string()
|
||||
}
|
||||
});
|
||||
let args = line.split(' ').map(subst_env);
|
||||
|
||||
match Command::from_arg_iter(args) {
|
||||
match Command::parse(args) {
|
||||
Ok(cmd) => cmds.push(cmd),
|
||||
Err(err) => errors.push(err),
|
||||
}
|
||||
@@ -35,36 +39,29 @@ impl Script {
|
||||
#[derive(Debug)]
|
||||
pub enum Command {
|
||||
// Service
|
||||
Nowait(String, Vec<String>),
|
||||
Notify(String, Vec<String>),
|
||||
Scheme(String, String, Vec<String>),
|
||||
Regular(String, Vec<String>),
|
||||
Nowait(Process),
|
||||
Notify(Process),
|
||||
Scheme(String, Process),
|
||||
Regular(Process),
|
||||
|
||||
// Modify env
|
||||
Cd(PathBuf),
|
||||
Stdio(String),
|
||||
Export(String, String),
|
||||
Unset(Vec<String>),
|
||||
|
||||
// Misc
|
||||
Echo(String),
|
||||
RunD(Vec<PathBuf>),
|
||||
SwitchRoot(PathBuf, PathBuf),
|
||||
Nothing,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn from_arg_iter(mut args: impl Iterator<Item = String>) -> Result<Command, String> {
|
||||
fn parse(mut args: impl Iterator<Item = String>) -> Result<Command, String> {
|
||||
let Some(cmd) = args.next() else {
|
||||
return Ok(Command::Nothing);
|
||||
};
|
||||
|
||||
match cmd.as_str() {
|
||||
"cd" => {
|
||||
let Some(dir) = args.next() else {
|
||||
return Err("init: failed to cd: no argument".to_owned());
|
||||
};
|
||||
Ok(Command::Cd(PathBuf::from(dir)))
|
||||
}
|
||||
"echo" => Ok(Command::Echo(args.collect::<Vec<_>>().join(" "))),
|
||||
"export" => {
|
||||
let Some(var) = args.next() else {
|
||||
@@ -80,15 +77,17 @@ impl Command {
|
||||
}
|
||||
Ok(Command::Export(var, value))
|
||||
}
|
||||
"run.d" => {
|
||||
let args = args.map(|arg| PathBuf::from(arg)).collect::<Vec<_>>();
|
||||
if args.is_empty() {
|
||||
return Err(
|
||||
"init: failed to run.d: no argument or all dirs are non-existent"
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
Ok(Command::RunD(args))
|
||||
"switchroot" => {
|
||||
let Some(prefix) = args.next() else {
|
||||
return Err("init: failed to switchroot: no argument".to_owned());
|
||||
};
|
||||
let Some(etcdir) = args.next() else {
|
||||
return Err("init: failed to switchroot: missing etcdir".to_owned());
|
||||
};
|
||||
Ok(Command::SwitchRoot(
|
||||
PathBuf::from(prefix),
|
||||
PathBuf::from(etcdir),
|
||||
))
|
||||
}
|
||||
"stdio" => {
|
||||
let Some(stdio) = args.next() else {
|
||||
@@ -97,31 +96,65 @@ impl Command {
|
||||
Ok(Command::Stdio(stdio))
|
||||
}
|
||||
"unset" => Ok(Command::Unset(args.collect())),
|
||||
"nowait" => {
|
||||
let Some(cmd) = args.next() else {
|
||||
return Err("init: failed to run nowait: no argument".to_owned());
|
||||
};
|
||||
|
||||
Ok(Command::Nowait(cmd, args.collect()))
|
||||
}
|
||||
"notify" => {
|
||||
let Some(cmd) = args.next() else {
|
||||
return Err("init: failed to run notify: no argument".to_owned());
|
||||
};
|
||||
|
||||
Ok(Command::Notify(cmd, args.collect()))
|
||||
}
|
||||
"nowait" => Ok(Command::Nowait(Process::parse(args)?)),
|
||||
"notify" => Ok(Command::Notify(Process::parse(args)?)),
|
||||
"scheme" => {
|
||||
let Some(scheme) = args.next() else {
|
||||
return Err("init: failed to run scheme: no argument".to_owned());
|
||||
};
|
||||
let Some(cmd) = args.next() else {
|
||||
return Err("init: failed to run scheme: missing command".to_owned());
|
||||
};
|
||||
|
||||
Ok(Command::Scheme(scheme, cmd, args.collect()))
|
||||
Ok(Command::Scheme(scheme, Process::parse(args)?))
|
||||
}
|
||||
_ => Ok(Command::Regular(cmd, args.collect())),
|
||||
_ => Ok(Command::Regular(Process::parse(
|
||||
iter::once(cmd).chain(args),
|
||||
)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Process {
|
||||
pub cmd: String,
|
||||
pub args: Vec<String>,
|
||||
pub envs: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
fn parse(parts: impl Iterator<Item = String>) -> Result<Process, String> {
|
||||
let mut cmd = None;
|
||||
let mut args = vec![];
|
||||
let mut envs = vec![];
|
||||
|
||||
for arg in parts {
|
||||
if cmd.is_none() {
|
||||
if let Some((env, value)) = arg.split_once('=') {
|
||||
let value = if value == "$" {
|
||||
env::var(env).unwrap_or_default()
|
||||
} else {
|
||||
subst_env(value)
|
||||
};
|
||||
if !value.is_empty() {
|
||||
envs.push((env.to_owned(), value));
|
||||
}
|
||||
} else {
|
||||
cmd = Some(arg);
|
||||
}
|
||||
} else {
|
||||
args.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
Ok(Process { cmd, args, envs })
|
||||
} else {
|
||||
Err("no command given".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_command(self, base_envs: &BTreeMap<String, OsString>) -> process::Command {
|
||||
let mut command = process::Command::new(self.cmd);
|
||||
command.args(self.args);
|
||||
command.env_clear().envs(base_envs).envs(self.envs);
|
||||
command
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user