Files
RedBear-OS/local/patches/base/P9-init-scheduler-completed.patch
T
vasilito f31522130f fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed
Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
2026-05-05 20:20:37 +01:00

351 lines
14 KiB
Diff

--- a/init/src/scheduler.rs 2026-05-04 23:20:24.735668104 +0100
+++ b/init/src/scheduler.rs 2026-05-05 09:36:09.955622123 +0100
@@ -1,11 +1,14 @@
-use std::collections::VecDeque;
+use std::collections::{BTreeSet, VecDeque};
use crate::InitConfig;
-use crate::color::{init_debug, init_error, init_info, status_ok, status_skip};
+use crate::color::{init_error, status_ok, status_skip};
use crate::unit::{Unit, UnitId, UnitKind, UnitStore};
+const SPAWN_BATCH_SIZE: usize = 50;
+
pub struct Scheduler {
pending: VecDeque<Job>,
+ completed: BTreeSet<UnitId>,
}
struct Job {
@@ -21,6 +24,7 @@
pub fn new() -> Scheduler {
Scheduler {
pending: VecDeque::new(),
+ completed: BTreeSet::new(),
}
}
@@ -29,10 +33,11 @@
unit_store: &mut UnitStore,
unit_id: UnitId,
) {
+ let id_str = unit_id.0.clone();
let mut errors = vec![];
self.schedule_start(unit_store, unit_id, &mut errors);
- for error in errors {
- init_error(&format!("{}", error));
+ for error in &errors {
+ init_error(&format!("{}: {}", id_str, error));
}
}
@@ -55,7 +60,14 @@
}
}
+ pub fn has_pending(&self) -> bool {
+ !self.pending.is_empty()
+ }
+
pub fn step(&mut self, unit_store: &mut UnitStore, init_config: &mut InitConfig) {
+ let mut defer_count: usize = 0;
+ let mut spawned_this_step: usize = 0;
+
'a: loop {
let Some(job) = self.pending.pop_front() else {
return;
@@ -63,18 +75,44 @@
match job.kind {
JobKind::Start => {
- let unit = unit_store.unit_mut(&job.unit);
-
- for dep in &unit.info.requires_weak {
- for pending_job in &self.pending {
- if &pending_job.unit == dep {
- self.pending.push_back(job);
- continue 'a;
+ let deps_ok = {
+ let unit = unit_store.unit(&job.unit);
+ let mut ok = true;
+ for dep in &unit.info.requires_weak {
+ if self.completed.contains(dep) {
+ continue;
+ }
+ if !unit_store.has_unit(dep) {
+ continue;
}
+ let in_pending = self.pending.iter().any(|pj| &pj.unit == dep);
+ if in_pending {
+ ok = false;
+ break;
+ }
+ }
+ ok
+ };
+
+ if !deps_ok {
+ defer_count += 1;
+ self.pending.push_back(job);
+ if defer_count > self.pending.len() + self.completed.len() {
+ return;
}
+ continue 'a;
}
+ defer_count = 0;
+
+ let unit = unit_store.unit_mut(&job.unit);
run(unit, init_config);
+ self.completed.insert(job.unit);
+ spawned_this_step += 1;
+
+ if spawned_this_step >= SPAWN_BATCH_SIZE {
+ return;
+ }
}
}
}
@@ -85,9 +123,6 @@
match &unit.kind {
UnitKind::LegacyScript { script } => {
for cmd in script.clone() {
- if config.log_debug {
- init_debug(&format!("running: {cmd:?}"));
- }
cmd.run(config);
}
}
@@ -97,18 +132,9 @@
status_skip(&format!("Skipping {} ({})", desc, service.cmd));
return;
}
- if config.log_debug {
- init_info(&format!("Starting {} ({})", desc, service.cmd));
- } else {
- status_ok(&format!("Started {}", desc));
- }
+ status_ok(&format!("Started {}", desc));
service.spawn(&config.envs);
}
- UnitKind::Target {} => {
- if config.log_debug {
- init_info(&format!("Reached target {}",
- unit.info.description.as_ref().unwrap_or(&unit.id.0)));
- }
- }
+ UnitKind::Target {} => {}
}
}
--- a/init/src/service.rs 2026-05-04 23:20:24.736497941 +0100
+++ b/init/src/service.rs 2026-05-05 08:47:30.832599161 +0100
@@ -47,73 +47,91 @@
}
command.envs(base_envs).envs(&self.envs);
- let (mut read_pipe, write_pipe) = io::pipe().unwrap();
- unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) };
-
- let mut child = match command.spawn() {
- Ok(child) => child,
- Err(err) => {
- status_fail(&format!("failed to execute {:?}: {}", command, err));
- return;
- }
- };
-
match &self.type_ {
- ServiceType::Notify => match read_pipe.read_exact(&mut [0]) {
- Ok(()) => {}
- Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
- init_warn(&format!("{:?} exited without notifying readiness", command));
- }
- Err(err) => {
- init_error(&format!("failed to wait for {:?}: {}", command, err));
+ ServiceType::OneshotAsync => {
+ match command.spawn() {
+ Ok(_child) => {}
+ Err(err) => {
+ status_fail(&format!("failed to execute {:?}: {}", command, err));
+ }
}
- },
- ServiceType::Scheme(scheme) => {
- let mut new_fd = usize::MAX;
- loop {
- match syscall::call_ro(
- read_pipe.as_raw_fd() as usize,
- unsafe { plain::as_mut_bytes(&mut new_fd) },
- syscall::CallFlags::FD | syscall::CallFlags::FD_UPPER,
- &[],
- ) {
- Err(syscall::Error {
- errno: syscall::EINTR,
- }) => continue,
- Ok(0) => {
+ }
+ _ => {
+ let (mut read_pipe, write_pipe) = io::pipe().unwrap();
+ let write_fd: OwnedFd = write_pipe.into();
+ let write_raw = write_fd.as_raw_fd();
+ unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_fd) };
+
+ let mut child = match command.spawn() {
+ Ok(child) => child,
+ Err(err) => {
+ let _ = unsafe { libc::close(write_raw) };
+ drop(read_pipe);
+ status_fail(&format!("failed to execute {:?}: {}", command, err));
+ return;
+ }
+ };
+
+ let _ = unsafe { libc::close(write_raw) };
+
+ match &self.type_ {
+ ServiceType::Notify => match read_pipe.read_exact(&mut [0]) {
+ Ok(()) => {}
+ Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
init_warn(&format!("{:?} exited without notifying readiness", command));
- return;
- }
- Ok(1) => break,
- Ok(n) => {
- init_error(&format!("incorrect amount of fds {} returned", n));
- return;
}
Err(err) => {
init_error(&format!("failed to wait for {:?}: {}", command, err));
- return;
}
- }
- }
-
- let current_namespace_fd = libredox::call::getns().expect("TODO");
- libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
- .expect("TODO");
- }
- ServiceType::Oneshot => {
- drop(read_pipe);
- match child.wait() {
- Ok(exit_status) => {
- if !exit_status.success() {
- status_fail(&format!("{:?} failed with {}", command, exit_status));
+ },
+ ServiceType::Scheme(scheme) => {
+ let mut new_fd = usize::MAX;
+ loop {
+ match syscall::call_ro(
+ read_pipe.as_raw_fd() as usize,
+ unsafe { plain::as_mut_bytes(&mut new_fd) },
+ syscall::CallFlags::FD | syscall::CallFlags::FD_UPPER,
+ &[],
+ ) {
+ Err(syscall::Error {
+ errno: syscall::EINTR,
+ }) => continue,
+ Ok(0) => {
+ init_warn(&format!("{:?} exited without notifying readiness", command));
+ return;
+ }
+ Ok(1) => break,
+ Ok(n) => {
+ init_error(&format!("incorrect amount of fds {} returned", n));
+ return;
+ }
+ Err(err) => {
+ init_error(&format!("failed to wait for {:?}: {}", command, err));
+ return;
+ }
+ }
}
+
+ let current_namespace_fd = libredox::call::getns().expect("TODO");
+ libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
+ .expect("TODO");
}
- Err(err) => {
- init_error(&format!("failed to wait for {:?}: {}", command, err))
+ ServiceType::Oneshot => {
+ drop(read_pipe);
+ match child.wait() {
+ Ok(exit_status) => {
+ if !exit_status.success() {
+ status_fail(&format!("{:?} failed with {}", command, exit_status));
+ }
+ }
+ Err(err) => {
+ init_error(&format!("failed to wait for {:?}: {}", command, err))
+ }
+ }
}
+ ServiceType::OneshotAsync => unreachable!(),
}
}
- ServiceType::OneshotAsync => {}
}
}
}
@@ -122,7 +140,6 @@
cmd.env(env, format!("{}", fd.as_raw_fd()));
unsafe {
cmd.pre_exec(move || {
- // Pass notify pipe to child
if libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, 0) == -1 {
Err(io::Error::last_os_error())
} else {
--- a/init/src/unit.rs 2026-05-04 23:40:38.259604979 +0100
+++ b/init/src/unit.rs 2026-05-05 08:47:30.832676105 +0100
@@ -101,6 +101,10 @@
pub fn unit_mut(&mut self, unit: &UnitId) -> &mut Unit {
self.units.get_mut(unit).unwrap()
}
+
+ pub fn has_unit(&self, unit: &UnitId) -> bool {
+ self.units.contains_key(unit)
+ }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
--- a/init/src/main.rs 2026-05-04 23:20:24.737380326 +0100
+++ b/init/src/main.rs 2026-05-05 09:09:22.005434517 +0100
@@ -137,10 +137,16 @@
Path::new("/usr"),
Path::new("/etc"),
);
- {
- // FIXME introduce multi-user.target unit and replace the config dir iteration
- // scheduler.schedule_start_and_report_errors(&mut unit_store, UnitId("multi-user.target".to_owned()));
+ if let Some(skip_val) = init_config.envs.get("INIT_SKIP") {
+ if let Some(skip_str) = skip_val.to_str() {
+ if !skip_str.is_empty() {
+ init_config.skip_cmd = skip_str.split(',').map(|s| s.to_string()).collect();
+ }
+ }
+ }
+
+ {
let entries = match config::config_for_dirs(&unit_store.config_dirs) {
Ok(entries) => entries,
Err(err) => {
@@ -167,11 +173,14 @@
libredox::call::setrens(0, 0).expect("init: failed to enter null namespace");
loop {
- // Process any pending jobs whose dependencies may now be satisfied.
- scheduler.step(&mut unit_store, &mut init_config);
+ if scheduler.has_pending() {
+ // Reap exited children before processing more services.
+ let mut status = 0;
+ while libredox::call::waitpid(0, &mut status, 1).is_ok() {}
+
+ scheduler.step(&mut unit_store, &mut init_config);
+ }
- // Non-blocking wait: WNOHANG = 1. Reap any exited children without
- // blocking, then loop back to process any newly-unblocked pending jobs.
let mut status = 0;
match libredox::call::waitpid(0, &mut status, 1) {
Ok(_pid) => {}