From 0227c0677ce63d04891d051b868b083535e4c024 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:18:07 +0100 Subject: [PATCH 1/5] init: Introduce switchroot command This sets PATH, LD_LIBRARY_PATH and runs init scripts for the new root. In the future this could replace the entire set of services that should run, restarting any services for which a newer executable is available in the new root and stopping any services which don't exist in the new root. This behavior could also be useful for soft-reboots in the future to handle updates without rebooting the entire system. --- bootstrap/src/exec.rs | 1 + init.d/00_runtime | 6 +---- init.d/90_exit_initfs | 4 +--- init/src/main.rs | 56 +++++++++++++++++++++++-------------------- init/src/script.rs | 22 +++++++++-------- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/bootstrap/src/exec.rs b/bootstrap/src/exec.rs index d5100fe5bc..33bb588455 100644 --- a/bootstrap/src/exec.rs +++ b/bootstrap/src/exec.rs @@ -100,6 +100,7 @@ pub fn main() -> ! { .filter(|var| !var.starts_with(b"INITFS_")) .collect::>() }; + envs.push(b"RUST_BACKTRACE=1"); //envs.push(b"LD_DEBUG=all"); envs.push(b"LD_LIBRARY_PATH=/scheme/initfs/lib"); diff --git a/init.d/00_runtime b/init.d/00_runtime index 58973a9c23..d7d4e3d10f 100644 --- a/init.d/00_runtime +++ b/init.d/00_runtime @@ -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 diff --git a/init.d/90_exit_initfs b/init.d/90_exit_initfs index 80f22bad22..64291d12dd 100644 --- a/init.d/90_exit_initfs +++ b/init.d/90_exit_initfs @@ -1,4 +1,2 @@ cd / -export PATH /usr/bin -export LD_LIBRARY_PATH /usr/lib -run.d /usr/lib/init.d /etc/init.d +switchroot /usr /etc diff --git a/init/src/main.rs b/init/src/main.rs index 9ff4cbacb3..7699b6015b 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -42,6 +42,29 @@ impl InitConfig { } } +fn switch_root(prefix: &Path, etcdir: &Path, config: &InitConfig) { + unsafe { + env::set_var("PATH", prefix.join("bin")); + env::set_var("LD_LIBRARY_PATH", prefix.join("lib")); + } + 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: &InitConfig) -> Result<()> { let (script, errors) = script::Script::from_file(file)?; @@ -69,20 +92,8 @@ fn run_command(cmd: Command, config: &InitConfig) { } 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) { @@ -166,18 +177,11 @@ 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); - } - } + switch_root( + Path::new("/scheme/initfs"), + Path::new("/scheme/initfs/etc"), + &init_config, + ); libredox::call::setrens(0, 0).expect("init: failed to enter null namespace"); diff --git a/init/src/script.rs b/init/src/script.rs index 50d8ae2e5f..8c1b30772c 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -48,7 +48,7 @@ pub enum Command { // Misc Echo(String), - RunD(Vec), + SwitchRoot(PathBuf, PathBuf), Nothing, } @@ -80,15 +80,17 @@ impl Command { } Ok(Command::Export(var, value)) } - "run.d" => { - let args = args.map(|arg| PathBuf::from(arg)).collect::>(); - 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 { From 029f56eebedd705205e3eed6d4a4e314625c9c09 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:26:51 +0100 Subject: [PATCH 2/5] init: Remove cd command The working directory doesn't matter for services and login sessions already cd to the user home directory. This removes one source of global state manipulation. --- init.d/90_exit_initfs | 1 - init/src/main.rs | 5 ----- init/src/script.rs | 7 ------- 3 files changed, 13 deletions(-) diff --git a/init.d/90_exit_initfs b/init.d/90_exit_initfs index 64291d12dd..dea1136bd9 100644 --- a/init.d/90_exit_initfs +++ b/init.d/90_exit_initfs @@ -1,2 +1 @@ -cd / switchroot /usr /etc diff --git a/init/src/main.rs b/init/src/main.rs index 7699b6015b..6035b226f3 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -85,11 +85,6 @@ fn run(file: &Path, config: &InitConfig) -> Result<()> { fn run_command(cmd: Command, config: &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::SwitchRoot(prefix, etcdir) => { diff --git a/init/src/script.rs b/init/src/script.rs index 8c1b30772c..c118ce888d 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -41,7 +41,6 @@ pub enum Command { Regular(String, Vec), // Modify env - Cd(PathBuf), Stdio(String), Export(String, String), Unset(Vec), @@ -59,12 +58,6 @@ impl Command { }; 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::>().join(" "))), "export" => { let Some(var) = args.next() else { From b7524c3a65b21e12827b0ffd737989ef6f9fe780 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:50:48 +0100 Subject: [PATCH 3/5] init: Allow passing env vars to individual services --- init.d.aarch64-unknown-redox/40_drivers | 2 +- init.d/20_graphics | 2 +- init.d/40_drivers | 2 +- init.d/50_rootfs | 2 +- init/src/main.rs | 51 +++++------- init/src/script.rs | 101 ++++++++++++++++-------- 6 files changed, 93 insertions(+), 67 deletions(-) diff --git a/init.d.aarch64-unknown-redox/40_drivers b/init.d.aarch64-unknown-redox/40_drivers index fcfa633d87..e0eb8e2b1c 100644 --- a/init.d.aarch64-unknown-redox/40_drivers +++ b/init.d.aarch64-unknown-redox/40_drivers @@ -1,3 +1,3 @@ notify hwd -unset RSDP_ADDR RSDP_SIZE +notify RSDP_ADDR=$ RSDP_SIZE=$ hwd pcid-spawner --initfs diff --git a/init.d/20_graphics b/init.d/20_graphics index 414b0b4d97..bf0719ca9e 100644 --- a/init.d/20_graphics +++ b/init.d/20_graphics @@ -1,5 +1,5 @@ scheme input inputd -notify vesad +notify FRAMEBUFFER_ADDR=$ FRAMEBUFFER_VIRT=$ FRAMEBUFFER_WIDTH=$ FRAMEBUFFER_HEIGHT=$ FRAMEBUFFER_STRIDE=$ vesad unset FRAMEBUFFER_ADDR FRAMEBUFFER_VIRT FRAMEBUFFER_WIDTH FRAMEBUFFER_HEIGHT FRAMEBUFFER_STRIDE #TODO: unset FRAMEBUFFER1 and beyond? scheme fbbootlog fbbootlogd diff --git a/init.d/40_drivers b/init.d/40_drivers index 8f3fa01e71..4427519a94 100644 --- a/init.d/40_drivers +++ b/init.d/40_drivers @@ -1,4 +1,4 @@ notify ps2d -notify hwd +notify RSDP_ADDR=$ RSDP_SIZE=$ hwd unset RSDP_ADDR RSDP_SIZE pcid-spawner --initfs diff --git a/init.d/50_rootfs b/init.d/50_rootfs index 9102f1fa6d..07baec4e43 100644 --- a/init.d/50_rootfs +++ b/init.d/50_rootfs @@ -1,2 +1,2 @@ -redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK +REDOXFS_PASSWORD_ADDR=$ REDOXFS_PASSWORD_SIZE=$ redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK unset REDOXFS_UUID REDOXFS_BLOCK REDOXFS_PASSWORD_ADDR REDOXFS_PASSWORD_SIZE diff --git a/init/src/main.rs b/init/src/main.rs index 6035b226f3..de8a81bc67 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,7 +1,6 @@ use std::env; use std::io::Result; use std::path::Path; -use std::process; use libredox::flag::{O_RDONLY, O_WRONLY}; @@ -100,70 +99,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(); 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(); 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(); 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(); + 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) } } } diff --git a/init/src/script.rs b/init/src/script.rs index c118ce888d..ad03a289bc 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -1,5 +1,13 @@ 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); @@ -14,15 +22,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,10 +37,10 @@ impl Script { #[derive(Debug)] pub enum Command { // Service - Nowait(String, Vec), - Notify(String, Vec), - Scheme(String, String, Vec), - Regular(String, Vec), + Nowait(Process), + Notify(Process), + Scheme(String, Process), + Regular(Process), // Modify env Stdio(String), @@ -52,7 +54,7 @@ pub enum Command { } impl Command { - fn from_arg_iter(mut args: impl Iterator) -> Result { + fn parse(mut args: impl Iterator) -> Result { let Some(cmd) = args.next() else { return Ok(Command::Nothing); }; @@ -92,31 +94,64 @@ 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, + pub envs: Vec<(String, String)>, +} + +impl Process { + fn parse(parts: impl Iterator) -> Result { + 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) -> process::Command { + let mut command = process::Command::new(self.cmd); + command.args(self.args).envs(self.envs); + command + } +} From 87abd3fb703b6b23cb95d3eb264ff627150235a1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 19 Feb 2026 21:06:53 +0100 Subject: [PATCH 4/5] init: Maintain default service env vars separate from init env vars With this only the env vars set using export or explicitly specify for a service are passed to services (in addition to RUST_BACKTRACE, PATH and LD_LIBRARY_PATH). As such we no longer need to manually unset bootloader env vars to keep the environment clean. And finally this means that env var substitutions in shell scripts always use the bootloader provided env vars rather than locally specified ones. This does regress multi monitor support when there is no proper graphics driver. --- init.d/20_graphics | 2 -- init.d/40_drivers | 1 - init.d/50_rootfs | 1 - init/src/main.rs | 34 +++++++++++++++++++++------------- init/src/script.rs | 7 +++++-- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/init.d/20_graphics b/init.d/20_graphics index bf0719ca9e..ecfd423b4c 100644 --- a/init.d/20_graphics +++ b/init.d/20_graphics @@ -1,7 +1,5 @@ scheme input inputd notify FRAMEBUFFER_ADDR=$ FRAMEBUFFER_VIRT=$ FRAMEBUFFER_WIDTH=$ FRAMEBUFFER_HEIGHT=$ FRAMEBUFFER_STRIDE=$ vesad -unset FRAMEBUFFER_ADDR FRAMEBUFFER_VIRT FRAMEBUFFER_WIDTH FRAMEBUFFER_HEIGHT FRAMEBUFFER_STRIDE -#TODO: unset FRAMEBUFFER1 and beyond? scheme fbbootlog fbbootlogd # Activate framebuffer log VT, which disables kernel graphical debug inputd -A 1 diff --git a/init.d/40_drivers b/init.d/40_drivers index 4427519a94..82daf676c1 100644 --- a/init.d/40_drivers +++ b/init.d/40_drivers @@ -1,4 +1,3 @@ notify ps2d notify RSDP_ADDR=$ RSDP_SIZE=$ hwd -unset RSDP_ADDR RSDP_SIZE pcid-spawner --initfs diff --git a/init.d/50_rootfs b/init.d/50_rootfs index 07baec4e43..ece2c67124 100644 --- a/init.d/50_rootfs +++ b/init.d/50_rootfs @@ -1,2 +1 @@ REDOXFS_PASSWORD_ADDR=$ REDOXFS_PASSWORD_SIZE=$ redoxfs --uuid $REDOXFS_UUID file $REDOXFS_BLOCK -unset REDOXFS_UUID REDOXFS_BLOCK REDOXFS_PASSWORD_ADDR REDOXFS_PASSWORD_SIZE diff --git a/init/src/main.rs b/init/src/main.rs index de8a81bc67..27afab8cad 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,4 +1,6 @@ +use std::collections::BTreeMap; use std::env; +use std::ffi::OsString; use std::io::Result; use std::path::Path; @@ -23,6 +25,7 @@ fn switch_stdio(stdio: &str) -> Result<()> { struct InitConfig { log_debug: bool, skip_cmd: Vec, + envs: BTreeMap, } impl InitConfig { @@ -37,15 +40,20 @@ impl InitConfig { Self { log_debug, skip_cmd, + envs: BTreeMap::from([("RUST_BACKTRACE".to_owned(), "1".into())]), } } } -fn switch_root(prefix: &Path, etcdir: &Path, config: &InitConfig) { - unsafe { - env::set_var("PATH", prefix.join("bin")); - env::set_var("LD_LIBRARY_PATH", prefix.join("lib")); - } +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"), @@ -64,7 +72,7 @@ fn switch_root(prefix: &Path, etcdir: &Path, config: &InitConfig) { } } -fn run(file: &Path, config: &InitConfig) -> Result<()> { +fn run(file: &Path, config: &mut InitConfig) -> Result<()> { let (script, errors) = script::Script::from_file(file)?; for error in errors { @@ -81,7 +89,7 @@ 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::Echo(text) => println!("{text}"), @@ -105,7 +113,7 @@ fn run_command(cmd: Command, config: &InitConfig) { return; } - let mut command = cmd.into_command(); + let mut command = cmd.into_command(&config.envs); match command.spawn() { Ok(_child) => {} @@ -118,7 +126,7 @@ fn run_command(cmd: Command, config: &InitConfig) { return; } - let command = cmd.into_command(); + let command = cmd.into_command(&config.envs); daemon::Daemon::spawn(command); } @@ -128,7 +136,7 @@ fn run_command(cmd: Command, config: &InitConfig) { return; } - let command = cmd.into_command(); + let command = cmd.into_command(&config.envs); daemon::SchemeDaemon::spawn(command, &scheme); } @@ -138,7 +146,7 @@ fn run_command(cmd: Command, config: &InitConfig) { return; } - let mut command = cmd.into_command(); + let mut command = cmd.into_command(&config.envs); let mut child = match command.spawn() { Ok(child) => child, @@ -162,11 +170,11 @@ fn run_command(cmd: Command, config: &InitConfig) { } fn main() { - let init_config = InitConfig::new(); + let mut init_config = InitConfig::new(); switch_root( Path::new("/scheme/initfs"), Path::new("/scheme/initfs/etc"), - &init_config, + &mut init_config, ); libredox::call::setrens(0, 0).expect("init: failed to enter null namespace"); diff --git a/init/src/script.rs b/init/src/script.rs index ad03a289bc..c22ee99b19 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; +use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::{env, fs, io, iter, process}; @@ -149,9 +151,10 @@ impl Process { } } - pub fn into_command(self) -> process::Command { + pub fn into_command(self, base_envs: &BTreeMap) -> process::Command { let mut command = process::Command::new(self.cmd); - command.args(self.args).envs(self.envs); + command.args(self.args); + command.env_clear().envs(base_envs).envs(self.envs); command } } From 0f0f12fb1231abeb812caf7ee3735dfa66a8a445 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 19 Feb 2026 21:49:21 +0100 Subject: [PATCH 5/5] graphics/vesad: Fix multi-monitor support --- drivers/graphics/vesad/src/main.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/graphics/vesad/src/main.rs b/drivers/graphics/vesad/src/main.rs index bf605cfe9a..ac87e0b9db 100644 --- a/drivers/graphics/vesad/src/main.rs +++ b/drivers/graphics/vesad/src/main.rs @@ -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::>(); 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, }; }