fix(base): comprehensive daemon fix replacing conflicting patch chain

The previous daemon patches (P0-daemon-init-notify-graceful, P0-daemon-fix-init-notify-unwrap,
P0-daemon-silence-init-notify) were conflicting with each other and with
the upstream daemon code. The result was either:
1. get_fd returns RawFd (-1) but Daemon.write_pipe is Option<PipeWriter>
   (from P0-daemon-init-notify-graceful) — broken
2. get_fd returns Option<RawFd> but Daemon::new uses io::PipeWriter::from_raw_fd
   (no .map()) — broken

This single comprehensive patch applies all needed changes atomically:
- get_fd returns Option<RawFd> with full error handling
- Daemon.write_pipe: Option<PipeWriter>
- Daemon::new uses .map(|fd| unsafe { from_raw_fd(fd) })
- Daemon::ready handles Option
- SchemeDaemon.write_pipe: Option<PipeWriter>
- SchemeDaemon::new uses same closure pattern
- ready_with_fd handles Option

Replaces: P0-daemon-init-notify-graceful, P0-daemon-fix-init-notify-unwrap,
P0-daemon-silence-init-notify
This commit is contained in:
2026-06-18 10:48:56 +03:00
parent e460612120
commit 6c440d0d03
3 changed files with 24 additions and 94 deletions
@@ -1,21 +1,13 @@
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
index 9f507221..2d092b0b 100644
--- a/daemon/src/lib.rs
+++ b/daemon/src/lib.rs
@@ -10,26 +10,35 @@ use libredox::Fd;
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
@@ -10,15 +10,35 @@ use libredox::Fd;
use redox_scheme::Socket;
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
-unsafe fn get_fd(var: &str) -> RawFd {
- let fd: RawFd = match std::env::var(var)
- .map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
- .ok()
- .and_then(|val| {
- val.parse()
- .map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}"))
- .ok()
- }) {
- Some(fd) => fd,
- None => return -1,
- let fd: RawFd = std::env::var(var).unwrap().parse().unwrap();
+unsafe fn get_fd(var: &str) -> Option<RawFd> {
+ let value = match std::env::var(var) {
+ Ok(value) => value,
@@ -36,13 +28,13 @@ diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
+ eprintln!("daemon: invalid {var} value {value:?} for {exe}: {err}; readiness notification disabled");
+ return None;
+ }
};
+ };
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
eprintln!(
- panic!(
+ eprintln!(
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
io::Error::last_os_error()
);
- return -1;
+ return None;
}
- fd
@@ -50,7 +42,7 @@ diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
}
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
@@ -47,22 +56,20 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
@@ -38,20 +58,22 @@ unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
/// A long running background process that handles requests.
#[must_use = "Daemon::ready must be called"]
pub struct Daemon {
@@ -62,23 +54,21 @@ diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
/// Create a new daemon.
pub fn new(f: impl FnOnce(Daemon) -> !) -> ! {
- let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
+ let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
+ let write_pipe = get_fd("INIT_NOTIFY").map(|fd| unsafe { io::PipeWriter::from_raw_fd(fd) });
f(Daemon { write_pipe })
}
/// Notify the process that the daemon is ready to accept requests.
pub fn ready(mut self) {
- if let Err(err) = self.write_pipe.write_all(&[0]) {
- if err.kind() != io::ErrorKind::BrokenPipe {
- eprintln!("daemon::ready write failed: {err}");
- }
- self.write_pipe.write_all(&[0]).unwrap();
+ if let Some(write_pipe) = self.write_pipe.as_mut() {
+ write_pipe.write_all(&[0]).unwrap();
}
+ }
}
@@ -87,24 +94,26 @@ impl Daemon {
/// Executes `Command` as a child process.
@@ -83,21 +105,25 @@ impl Daemon {
/// A long running background process that handles requests using schemes.
#[must_use = "SchemeDaemon::ready must be called"]
pub struct SchemeDaemon {
@@ -90,26 +80,20 @@ diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
/// Create a new daemon for use with schemes.
pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! {
- let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) };
+ let write_pipe = unsafe { get_fd("INIT_NOTIFY").map(io::PipeWriter::from_raw_fd) };
+ let write_pipe = get_fd("INIT_NOTIFY").map(|fd| unsafe { io::PipeWriter::from_raw_fd(fd) });
f(SchemeDaemon { write_pipe })
}
/// Notify the process that the scheme daemon is ready to accept requests.
pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> {
- syscall::call_wo(
+ let write_pipe = match self.write_pipe {
+ Some(pipe) => pipe,
+ None => return Ok(()),
+ };
syscall::call_wo(
- self.write_pipe.as_raw_fd() as usize,
- &cap_fd.into_raw().to_ne_bytes(),
- syscall::CallFlags::FD,
- &[],
- )?;
+ if let Some(write_pipe) = self.write_pipe {
+ syscall::call_wo(
+ write_pipe.as_raw_fd() as usize,
+ &cap_fd.into_raw().to_ne_bytes(),
+ syscall::CallFlags::FD,
+ &[],
+ )?;
+ }
Ok(())
}
+ write_pipe.as_raw_fd() as usize,
&cap_fd.into_raw().to_ne_bytes(),
syscall::CallFlags::FD,
&[],
@@ -1,55 +0,0 @@
From: Red Bear OS
Date: 2026-04-28
Subject: daemon: handle missing INIT_NOTIFY gracefully instead of panicking
The Daemon::new() and Daemon::ready() functions in the daemon library
called unwrap() on the INIT_NOTIFY environment variable and the ready
pipe write, causing a hard panic when a daemon is started outside the
init system's notification pipe mechanism.
Replace unwrap() with graceful error handling:
- get_fd() returns -1 if the env var is missing or invalid, logging
a warning via eprintln
- ready() logs a warning on write failure instead of panicking
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
index 9f507221..a0ba9d88 100644
--- a/daemon/src/lib.rs
+++ b/daemon/src/lib.rs
@@ -11,12 +11,23 @@ use redox_scheme::Socket;
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
unsafe fn get_fd(var: &str) -> RawFd {
- let fd: RawFd = std::env::var(var).unwrap().parse().unwrap();
+ let fd: RawFd = match std::env::var(var)
+ .map_err(|e| eprintln!("daemon: env var {var} not set: {e}"))
+ .ok()
+ .and_then(|val| {
+ val.parse()
+ .map_err(|e| eprintln!("daemon: failed to parse {var} as fd: {e}"))
+ .ok()
+ }) {
+ Some(fd) => fd,
+ None => return -1,
+ };
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 {
- panic!(
+ eprintln!(
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
io::Error::last_os_error()
);
+ return -1;
}
fd
}
@@ -50,7 +61,9 @@ impl Daemon {
/// Notify the process that the daemon is ready to accept requests.
pub fn ready(mut self) {
- self.write_pipe.write_all(&[0]).unwrap();
+ if let Err(err) = self.write_pipe.write_all(&[0]) {
+ eprintln!("daemon::ready write failed: {err}");
+ }
}
/// Executes `Command` as a child process.
+1
View File
@@ -3,6 +3,7 @@ git = "https://gitlab.redox-os.org/redox-os/base.git"
rev = "463f76b9608a896e6f6c9f63457f57f6409873c7"
patches = [
"P0-redox-scheme-bump-0.11.1.patch",
"P0-daemon-comprehensive-fix.patch",
"P0-add-missing-driver-members.patch",
"P0-init-continuous-scheduling.patch",
"P0-dhcpd-auto-iface.patch",