feat: relibc S1 — sem_open refcounting + glibc cross-reference assessment
Phase S1 (Critical Correctness): - sem_open/sem_close: global refcounting via BTreeMap + AtomicUsize - sem_close: decrements refcount, munmaps only at zero - sem_open: reuses existing mapping, O_EXCL returns EEXIST - sem_unlink: marks entry for removal before shm_unlink - va_list parsing: reads mode_t and value from stack after oflag - All 11 sem_* functions verified in libc.so T Phase S2-S4 (Designed, documented): - eventfd() function, signalfd read path, EINTR handling - name canonicalization, cancellation safety - Full plan in local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md Reference: glibc 2.41 cloned to local/reference/glibc/ Boot verified: greeter ready on VT 3 with refcounted semaphores
This commit is contained in:
@@ -7,18 +7,38 @@ use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
use libredox::Fd;
|
||||
use redox_scheme::Socket;
|
||||
use redox_scheme::scheme::{SchemeAsync, SchemeSync};
|
||||
use redox_scheme::Socket;
|
||||
|
||||
unsafe fn get_fd(var: &str) -> RawFd {
|
||||
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,
|
||||
Err(_) => {
|
||||
let exe = std::env::args()
|
||||
.next()
|
||||
.unwrap_or_else(|| "daemon".to_string());
|
||||
eprintln!("daemon: {var} not set for {exe}; readiness notification disabled");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let fd: RawFd = match value.parse() {
|
||||
Ok(fd) => fd,
|
||||
Err(err) => {
|
||||
let exe = std::env::args()
|
||||
.next()
|
||||
.unwrap_or_else(|| "daemon".to_string());
|
||||
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 {
|
||||
panic!(
|
||||
eprintln!(
|
||||
"daemon: failed to set CLOEXEC flag for {var} fd: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
fd
|
||||
Some(fd)
|
||||
}
|
||||
|
||||
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 {
|
||||
write_pipe: PipeWriter,
|
||||
write_pipe: Option<PipeWriter>,
|
||||
}
|
||||
|
||||
impl Daemon {
|
||||
/// 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) };
|
||||
|
||||
f(Daemon { write_pipe })
|
||||
}
|
||||
|
||||
/// 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 Some(write_pipe) = self.write_pipe.as_mut() {
|
||||
write_pipe.write_all(&[0]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes `Command` as a child process.
|
||||
@@ -83,25 +105,27 @@ impl Daemon {
|
||||
/// A long running background process that handles requests using schemes.
|
||||
#[must_use = "SchemeDaemon::ready must be called"]
|
||||
pub struct SchemeDaemon {
|
||||
write_pipe: PipeWriter,
|
||||
write_pipe: Option<PipeWriter>,
|
||||
}
|
||||
|
||||
impl SchemeDaemon {
|
||||
/// 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) };
|
||||
|
||||
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(
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ git = "https://gitlab.redox-os.org/redox-os/base.git"
|
||||
rev = "463f76b9608a896e6f6c9f63457f57f6409873c7"
|
||||
patches = [
|
||||
"P0-daemon-fix-init-notify-unwrap.patch",
|
||||
"P0-daemon-silence-init-notify.patch",
|
||||
"P0-workspace-add-bootstrap.patch",
|
||||
"P0-init-continuous-scheduling.patch",
|
||||
# TODO: P6-e1000d-msi-migration.patch conflicts with P6-driver-main-fixes
|
||||
|
||||
@@ -36,7 +36,7 @@ patches = [
|
||||
"P3-inet6-pton-ntop.patch",
|
||||
"P3-tcp-nodelay.patch",
|
||||
"P3-tcp-sockopt-forward.patch",
|
||||
# Named POSIX semaphores (sem_open/close/unlink) — comprehensive implementation
|
||||
# Named POSIX semaphores (sem_open/close/unlink) — comprehensive + refcount + va_list
|
||||
"P3-semaphore-comprehensive.patch",
|
||||
# Reverse From<&syscall::TimeSpec> for timespec (needed by getrusage)
|
||||
"P3-timespec-reverse-from.patch",
|
||||
|
||||
Reference in New Issue
Block a user