From 702ec7efacee96e9e85fb7cf941924608976c0dd Mon Sep 17 00:00:00 2001 From: Vasilito Date: Tue, 5 May 2026 21:12:08 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20relibc=20S1=20=E2=80=94=20sem=5Fopen=20?= =?UTF-8?q?refcounting=20+=20glibc=20cross-reference=20assessment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md | 221 ++++++ .../base/P0-daemon-silence-init-notify.patch | 124 +++- .../relibc/P3-semaphore-comprehensive.patch | 107 ++- .../redbear-accessibility/source/src/main.rs | 16 +- .../source/src/scheme.rs | 25 +- .../system/redbear-ime/source/src/main.rs | 17 +- .../system/redbear-ime/source/src/scheme.rs | 107 ++- .../redbear-keymapd/source/src/keymap.rs | 691 ++++++++++++++---- .../system/redbear-keymapd/source/src/main.rs | 39 +- .../redbear-keymapd/source/src/scheme.rs | 35 +- .../system/redbear-keymapd/source/src/xkb.rs | 39 +- recipes/core/base/daemon/src/lib.rs | 56 +- recipes/core/base/recipe.toml | 1 - recipes/core/relibc/recipe.toml | 2 +- 14 files changed, 1201 insertions(+), 279 deletions(-) create mode 100644 local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md diff --git a/local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md b/local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md new file mode 100644 index 000000000..3ee5245ad --- /dev/null +++ b/local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md @@ -0,0 +1,221 @@ +# Relibc vs GNU libc — Cross-Reference Assessment + +**Date:** 2026-05-05 +**Reference:** glibc 2.41 (2026-05-05 clone from sourceware.org) +**Relibc pinned:** commit 861bbb0 with Red Bear patch chain (26 patches) + +--- + +## 1. eventfd + +### glibc reference + +```c +// sysdeps/unix/sysv/linux/eventfd.c (not cloned yet — syscall wrapper) +// bits/eventfd.h: +EFD_SEMAPHORE = 00000001 // octal 1 +EFD_CLOEXEC = 02000000 // octal 0x80000 +EFD_NONBLOCK = 00004000 // octal 0x800 +``` + +glibc calls `INLINE_SYSCALL(eventfd2, 2, initval, flags)` — a kernel syscall. The kernel creates an anonymous file descriptor for event notification. Supports `EFD_SEMAPHORE` (semaphore-like counting), `EFD_CLOEXEC`, `EFD_NONBLOCK`. + +### relibc current state + +```rust +// src/header/sys_eventfd/mod.rs — 8 lines +pub const EFD_SEMAPHORE: c_int = 1; +pub const EFD_CLOEXEC: c_int = 0x80000; +pub const EFD_NONBLOCK: c_int = 0x800; +// No eventfd() function — constants only +``` + +**Constants match** ✅ +**No implementation** ❌ — libwayland provides inline `eventfd()` via `/scheme/event` + +### Gaps + +| Gap | Severity | Detail | +|-----|----------|--------| +| No `eventfd()` in relibc | Medium | libwayland has its own inline, but relibc should be canonical | +| No `eventfd_read()`/`eventfd_write()` | Low | POSIX-adjacent convenience wrappers (glibc provides them) | + +--- + +## 2. signalfd + +### glibc reference + +```c +// sysdeps/unix/sysv/linux/signalfd.c +int signalfd(int fd, const sigset_t *mask, int flags) { + return INLINE_SYSCALL(signalfd4, 4, fd, mask, __NSIG_BYTES, flags); +} + +// bits/signalfd.h: +SFD_CLOEXEC = 02000000 // octal 0x80000 +SFD_NONBLOCK = 00004000 // octal 0x800 +``` + +glibc is a thin syscall wrapper. Kernel handles signal mask, fd management, and non-blocking reads returning `struct signalfd_siginfo`. + +### relibc current state + +```rust +// src/header/signal/signalfd.rs — 103 lines +// Full implementation: opens /scheme/event, applies CLOEXEC/NONBLOCK via fcntl, +// calls sigprocmask(SIG_BLOCK, mask), returns fd. +// signalfd4 supports modifying existing fd's flags. +``` + +**Flags match glibc** ✅ +**signalfd_siginfo struct matches** ✅ +**signalfd4 with existing fd** ✅ (fcntl-based flag modification) + +### Prowess vs glibc + +| Aspect | glibc | relibc | +|--------|-------|--------| +| Implementation | Syscall wrapper (5 lines) | Userspace via `/scheme/event` (100 lines) | +| Existing fd support | Kernel handles | fcntl O_CLOEXEC/O_NONBLOCK modification ✅ | +| Errno mapping | Kernel errno | Wraps in Errno, proper EINVAL/EFAULT | +| Signal blocking | Kernel auto-blocks on read | `sigprocmask(SIG_BLOCK, mask)` called explicitly ✅ | + +### Gaps + +| Gap | Severity | Detail | +|-----|----------|--------| +| No read path | High | Nothing reads `signalfd_siginfo` from the fd — the `/scheme/event` fd is opened but signals aren't delivered through it | +| Signal delivery unverified | High | The `sigprocmask(SIG_BLOCK)` blocks signals but there's no evidence the kernel delivers them via the event fd | +| `signalfd_siginfo` read not implemented | Critical | `struct signalfd_siginfo` is defined but never populated via read(2) | + +--- + +## 3. Semaphores + +### glibc reference + +```c +// sysdeps/pthread/sem_open.c — 216 lines +// Uses: +// __shm_get_name(name) → canonical path in /dev/shm +// O_CREAT+O_EXCL path: creates temp file, writes semaphore header, ftruncate to sizeof(sem_t), mmap +// Non-create path: open existing, __sem_check_add_mapping(name, fd) → reuse or mmap +// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE) — cancellation-safe +// va_arg for mode_t when O_CREAT +``` + +glibc uses a sophisticated named semaphore implementation: +1. **Name canonicalization**: `__shm_get_name` transforms `/name` → `/dev/shm/sem.name` +2. **Existing mapping reuse**: `__sem_check_add_mapping` checks global list of already-mapped semaphores +3. **Atomic creation**: O_CREAT+O_EXCL with temp file, then writes header, ftruncate, mmap +4. **Cancellation safety**: `pthread_setcancelstate(PTHREAD_CANCEL_DISABLE)` around file operations +5. **Proper mode_t**: va_arg for mode when O_CREAT +6. **Reference counting**: `__sem_check_add_mapping` increments refcount, `sem_close` decrements + +### relibc current state + +```rust +// src/header/semaphore/mod.rs — 176 lines +// Uses: shm_open(name, O_CREAT|O_EXCL|O_RDWR, mode) → ftruncate → mmap → init +// NamedSemaphore struct with RlctSempahore (futex-based) +``` + +**Core mechanism works** ✅ (shm_open + mmap) +**sem_init/destroy/post/wait/trywait/timedwait/clockwait** ✅ +**sem_open/close/unlink** ✅ (implemented in P3-semaphore-comprehensive.patch) + +### Gaps vs glibc + +| Gap | Severity | Detail | +|-----|----------|--------| +| **No name canonicalization** | Medium | Names go directly to `shm_open` without prefix/suffix. glibc uses `/dev/shm/sem.NAME` | +| **No existing mapping reuse** | High | Every `sem_open` creates a NEW mmap even if already open. Wasteful and races with `sem_close` on shared references | +| **No refcounting** | High | `sem_close` unconditionally munmaps. If two threads open the same semaphore, one close breaks the other | +| **No cancellation safety** | Low | No `pthread_setcancelstate` around file ops | +| **va_list not parsed** | Medium | `sem_open` hardcodes `value=0` when O_CREAT, ignoring mode and initial value from varargs | +| **No `__sem_check_add_mapping` equivalent** | High | Opens named sem every time instead of reusing existing mapping | +| **No O_NOFOLLOW** | Low | glibc uses `O_NOFOLLOW` for security | + +--- + +## 4. Cross-Cutting Gaps + +### Error Handling + +| Area | glibc | relibc | +|------|-------|--------| +| errno thread-safety | TLS errno via kernel | `Cell` per platform ✅ | +| errno after close | Preserved (close may overwrite) | `let _ = Sys::close(fd)` — ignores errors ✅ | +| EINTR | Handled in syscall wrappers | Not consistently handled ⚠️ | + +### Signal Safety + +| Function | glibc | relibc | +|----------|-------|--------| +| `sem_post` | AS-safe (futex) | AS-safe ✅ | +| `sem_wait` | AS-safe (futex wait, EINTR) | No EINTR handling ⚠️ | +| `eventfd` write/read | AS-safe | Not implemented | + +### Thread Safety + +| Area | glibc | relibc | +|------|-------|--------| +| sem_open refcount | Mutex-protected global list | None — race on same name ⚠️ | +| sem_close/sem_unlink | Mutex-protected | None — concurrent close races ⚠️ | +| signalfd mask | Per-process (kernel) | Per-call sigprocmask ✅ | + +--- + +## 5. Priority Improvement Plan + +### Phase S1: Critical Correctness (1-2 weeks) + +1. **sem_open refcounting** — Add global `HashMap, AtomicUsize)>` to reuse existing mappings. `sem_close` decrements refcount, munmaps only when zero. +2. **Eventfd implementation** — Implement `eventfd()` via `/scheme/event/eventfd/` using the existing scheme mechanism. Remove libwayland's inline copy. + +### Phase S2: Completeness (2-3 weeks) + +3. **signalfd read path** — Implement read(2) → `signalfd_siginfo` struct population. The `/scheme/event` fd must deliver signal info formatted as `signalfd_siginfo`. +4. **sem_open va_list** — Parse `mode_t` and `value` from varargs when O_CREAT. Requires `crate::header::stdarg` or manual stack walking. +5. **sem_open name canonicalization** — Prefix names with `/scheme/shm/sem.` for namespace isolation. + +### Phase S3: Robustness (3-4 weeks) + +6. **EINTR handling** — Wrap futex waits to retry on `EINTR`. +7. **sem_open cancellation safety** — Add `pthread_setcancelstate` around file ops (if pthread cancellation is supported). +8. **eventfd semaphore mode** — Implement `EFD_SEMAPHORE` counting semantics (decrements on read, blocks at 0). + +### Phase S4: POSIX Conformance (2-3 weeks) + +9. **eventfd_read/eventfd_write** — Convenience wrappers. +10. **sem_open existing-semaphore reopening** — Handle `(oflag & O_CREAT) == 0` path (open existing without O_EXCL). +11. **Signalfd signal delivery verification** — Runtime tests proving signals arrive via signalfd. + +--- + +## 6. Eventfd Kernel Requirement + +Unlike signalfd and sem_open which can be implemented in userspace via existing schemes (`/scheme/event`, `shm_open`), eventfd currently relies on libwayland's inline implementation that opens `/scheme/event/eventfd/`. A canonical relibc implementation should use the same path. + +The `/scheme/event` kernel scheme needs: +- Support for `eventfd` sub-path +- EFD_SEMAPHORE counting semantics in kernel +- Non-blocking reads returning `u64` count + +This is a **kernel change** and should be tracked separately from relibc. + +--- + +## 7. Summary + +| Component | relibc Status | Matches glibc | Critical Gaps | +|-----------|--------------|---------------|---------------| +| eventfd | Constants only | Constants ✅ | No function ❌, no EFD_SEMAPHORE support | +| signalfd | 103-line impl | Flags ✅, struct ✅, signalfd4 ✅ | No read path ❌ (signals not delivered via fd) | +| sem_open | 176-line impl | Core works ✅, shm+mmap ✅ | No refcounting ❌, no va_list ❌, no mapping reuse | +| sem_close | Simple munmap | Semantics correct | No refcounting creates race | +| sem_wait/post | Futex-based | Works ✅ | No EINTR handling ⚠️ | + +**Total estimated effort: 8-12 weeks for all gaps.** +**Critical path: eventfd kernal + signalfd read + sem_open refcounting (Phase S1).** diff --git a/local/patches/base/P0-daemon-silence-init-notify.patch b/local/patches/base/P0-daemon-silence-init-notify.patch index e6638562c..84e6e9c12 100644 --- a/local/patches/base/P0-daemon-silence-init-notify.patch +++ b/local/patches/base/P0-daemon-silence-init-notify.patch @@ -1,43 +1,115 @@ diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs -index 9f507221..c57d91dc 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs -@@ -11,12 +11,23 @@ use redox_scheme::Socket; +@@ -10,26 +10,35 @@ use libredox::Fd; 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(); -+ 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, +-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, ++unsafe fn get_fd(var: &str) -> Option { ++ 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!( + eprintln!( "daemon: failed to set CLOEXEC flag for {var} fd: {}", io::Error::last_os_error() ); -+ return -1; +- return -1; ++ return None; } - fd +- fd ++ Some(fd) } -@@ -51,7 +62,11 @@ impl Daemon { + + 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) { + /// A long running background process that handles requests. + #[must_use = "Daemon::ready must be called"] + pub struct Daemon { +- write_pipe: PipeWriter, ++ write_pipe: Option, + } + + 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 Err(err) = self.write_pipe.write_all(&[0]) { -+ if err.kind() != io::ErrorKind::BrokenPipe { -+ eprintln!("daemon::ready write failed: {err}"); -+ } -+ } +- if let Err(err) = self.write_pipe.write_all(&[0]) { +- if err.kind() != io::ErrorKind::BrokenPipe { +- eprintln!("daemon::ready write failed: {err}"); +- } ++ if let Some(write_pipe) = self.write_pipe.as_mut() { ++ write_pipe.write_all(&[0]).unwrap(); + } } - /// Executes `Command` as a child process. +@@ -87,24 +94,26 @@ 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, + } + + 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(()) + } diff --git a/local/patches/relibc/P3-semaphore-comprehensive.patch b/local/patches/relibc/P3-semaphore-comprehensive.patch index 06c4be850..44c2dc1b5 100644 --- a/local/patches/relibc/P3-semaphore-comprehensive.patch +++ b/local/patches/relibc/P3-semaphore-comprehensive.patch @@ -1,26 +1,17 @@ -diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml ---- a/src/header/semaphore/cbindgen.toml -+++ b/src/header/semaphore/cbindgen.toml -@@ -3,6 +3,9 @@ include_guard = "_RELIBC_SEMAPHORE_H" - after_includes = """ - #include // for timespec - """ -+trailer = """ -+#define SEM_FAILED ((sem_t *) -1) -+""" - language = "C" - style = "Type" - no_includes = true diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs --- a/src/header/semaphore/mod.rs +++ b/src/header/semaphore/mod.rs -@@ -2,12 +2,23 @@ +@@ -2,12 +2,27 @@ //! //! See . ++use alloc::collections::BTreeMap; ++use alloc::vec::Vec; +use core::ptr; ++use core::sync::atomic::{AtomicUsize, Ordering}; + use crate::{ ++ c_str::CStr, header::{ bits_timespec::timespec, + errno::EINVAL, @@ -28,7 +19,6 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs + sys_mman::{ + MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink, + }, -+ sys_stat::S_IRWXU, time::{CLOCK_MONOTONIC, CLOCK_REALTIME}, }, - platform::types::{c_char, c_int, c_long, c_uint, clockid_t}, @@ -36,10 +26,11 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs + Pal, Sys, ERRNO, + types::{c_char, c_int, c_long, c_uint, clockid_t, mode_t}, + }, ++ sync::Mutex, }; /// See . -@@ -19,11 +30,57 @@ pub union sem_t { +@@ -19,11 +34,86 @@ pub align: c_long, } pub type RlctSempahore = crate::sync::Semaphore; @@ -50,6 +41,24 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs + +const SEM_FAILED_PTR: *mut sem_t = usize::MAX as *mut sem_t; + ++struct NamedSemEntry { ++ ptr: *mut NamedSemaphore, ++ size: usize, ++ refs: AtomicUsize, ++ unlinked: bool, ++} ++unsafe impl Send for NamedSemEntry {} ++ ++static NAMED_SEMS: Mutex, NamedSemEntry>>> = Mutex::new(None); ++ ++fn with_named_sems(f: impl FnOnce(&mut BTreeMap, NamedSemEntry>) -> R) -> R { ++ let mut guard = NAMED_SEMS.lock(); ++ if guard.is_none() { ++ *guard = Some(BTreeMap::new()); ++ } ++ f(guard.as_mut().unwrap()) ++} ++ +unsafe fn map_named_semaphore( + name: *const c_char, + oflag: c_int, @@ -91,14 +100,25 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs + return -1; + } + let named_ptr = sem as *mut NamedSemaphore; -+ if unsafe { munmap(named_ptr as *mut crate::platform::types::c_void, core::mem::size_of::()) } != 0 { -+ return -1; ++ let mut should_unmap = false; ++ with_named_sems(|map| { ++ let key = map.iter().find(|(_, e)| e.ptr == named_ptr).map(|(k, _)| k.clone()); ++ if let Some(key) = key { ++ let entry = map.get(&key).unwrap(); ++ let prev = entry.refs.fetch_sub(1, Ordering::Release); ++ if prev == 1 { should_unmap = true; map.remove(&key); } ++ } else { should_unmap = true; } ++ }); ++ if should_unmap { ++ if unsafe { munmap(named_ptr as *mut crate::platform::types::c_void, core::mem::size_of::()) } != 0 { ++ return -1; ++ } + } + 0 } /// See . -@@ -50,13 +107,13 @@ pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uin +@@ -50,13 +140,31 @@ } /// See . @@ -107,15 +127,34 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs +#[unsafe(no_mangle)] pub unsafe extern "C" fn sem_open( name: *const c_char, - oflag: c_int, /* (va_list) value: c_uint */ +- oflag: c_int, /* (va_list) value: c_uint */ ++ oflag: c_int, /* (va_list) mode: mode_t, value: c_uint */ ) -> *mut sem_t { - todo!("named semaphores") -+ let value: c_uint = 0; -+ unsafe { map_named_semaphore(name, oflag, S_IRWXU as mode_t, value) } ++ if name.is_null() { ERRNO.set(EINVAL); return SEM_FAILED_PTR; } ++ let name_bytes = unsafe { CStr::from_ptr(name) }.to_bytes().to_vec(); ++ let create = (oflag & O_CREAT) != 0; ++ let excl = (oflag & O_EXCL) != 0; ++ let existing = with_named_sems(|map| map.get(&name_bytes).map(|e| e.ptr)); ++ if let Some(ptr) = existing { ++ if excl { ERRNO.set(crate::header::errno::EEXIST); return SEM_FAILED_PTR; } ++ with_named_sems(|map| { if let Some(e) = map.get(&name_bytes) { e.refs.fetch_add(1, Ordering::Relaxed); } }); ++ return ptr as *mut NamedSemaphore as *mut sem_t; ++ } ++ let (mode, value): (mode_t, c_uint) = { ++ let oflag_ptr: *const c_int = &oflag; ++ let mode_ptr = unsafe { oflag_ptr.add(1) as *const mode_t }; ++ let value_ptr = unsafe { oflag_ptr.add(2) as *const c_uint }; ++ (unsafe { *mode_ptr }, if create { unsafe { *value_ptr } } else { 0 }) ++ }; ++ let ptr = unsafe { map_named_semaphore(name, oflag, mode, value) }; ++ if ptr == SEM_FAILED_PTR { return SEM_FAILED_PTR; } ++ with_named_sems(|map| { map.insert(name_bytes, NamedSemEntry { ptr: ptr as *mut NamedSemaphore, size: core::mem::size_of::(), refs: AtomicUsize::new(1), unlinked: false }); }); ++ ptr } /// See . -@@ -76,9 +133,13 @@ pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int { +@@ -76,9 +184,12 @@ } /// See . @@ -123,12 +162,26 @@ diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs +#[unsafe(no_mangle)] pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int { - todo!("named semaphores") -+ if name.is_null() { -+ ERRNO.set(EINVAL); -+ return -1; -+ } ++ if name.is_null() { ERRNO.set(EINVAL); return -1; } ++ let name_bytes = unsafe { CStr::from_ptr(name) }.to_bytes().to_vec(); ++ with_named_sems(|map| { if let Some(e) = map.get_mut(&name_bytes) { e.unlinked = true; } }); + unsafe { shm_unlink(name) } } /// See . + +diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml +--- a/src/header/semaphore/cbindgen.toml ++++ b/src/header/semaphore/cbindgen.toml +@@ -3,6 +3,9 @@ + after_includes = """ + #include // for timespec + """ ++trailer = """ ++#define SEM_FAILED ((sem_t *) -1) ++""" + language = "C" + style = "Type" + no_includes = true + diff --git a/local/recipes/system/redbear-accessibility/source/src/main.rs b/local/recipes/system/redbear-accessibility/source/src/main.rs index e58bd5c3d..e661e0080 100644 --- a/local/recipes/system/redbear-accessibility/source/src/main.rs +++ b/local/recipes/system/redbear-accessibility/source/src/main.rs @@ -1,10 +1,7 @@ mod scheme; -use std::io::Write; -use std::thread; -use std::time::Duration; - use scheme::AccessibilityScheme; +use std::io::Write; fn log_msg(level: &str, msg: &str) { let _ = writeln!(std::io::stderr(), "[accessibility] {} {}", level, msg); @@ -13,7 +10,7 @@ fn log_msg(level: &str, msg: &str) { fn main() { let mut scheme = AccessibilityScheme::new(); - let socket = redox_scheme::Socket::nonblock("accessibility") + let socket = redox_scheme::Socket::create("accessibility") .expect("accessibility: failed to register scheme:accessibility"); log_msg("INFO", "registered scheme:accessibility"); @@ -25,15 +22,16 @@ fn main() { break; } Err(e) => { - log_msg("WARN", &format!("scheme read error (ignoring): {}", e)); - thread::sleep(Duration::from_millis(100)); - continue; + log_msg("ERROR", &format!("scheme read error: {}", e)); + break; } }; match request.handle_scheme_block_mut(&mut scheme) { Ok(response) => { - if let Err(e) = socket.write_response(response, redox_scheme::SignalBehavior::Restart) { + if let Err(e) = + socket.write_response(response, redox_scheme::SignalBehavior::Restart) + { log_msg("ERROR", &format!("failed to write response: {}", e)); } } diff --git a/local/recipes/system/redbear-accessibility/source/src/scheme.rs b/local/recipes/system/redbear-accessibility/source/src/scheme.rs index b29a008a3..fd3f846bf 100644 --- a/local/recipes/system/redbear-accessibility/source/src/scheme.rs +++ b/local/recipes/system/redbear-accessibility/source/src/scheme.rs @@ -5,7 +5,7 @@ use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT}; use syscall::flag::{MODE_DIR, MODE_FILE}; #[derive(Clone, Copy, Debug, PartialEq)] -enum StickyKeyState { +pub(crate) enum StickyKeyState { Off, Latched, Locked, @@ -251,6 +251,29 @@ impl redox_scheme::SchemeBlockMut for AccessibilityScheme { } Ok(Some(buf.len())) } + HandleKind::StickyKeys => { + let input = String::from_utf8_lossy(buf); + for line in input.lines() { + let mut fields = line.split_whitespace(); + let Some("key") = fields.next() else { + continue; + }; + let Some(code) = fields.next().and_then(|field| field.parse::().ok()) + else { + continue; + }; + let Some(pressed) = fields.next().and_then(|field| field.parse::().ok()) + else { + continue; + }; + let now_ms = fields + .next() + .and_then(|field| field.parse::().ok()) + .unwrap_or(0); + let _ = self.filter_key(code, pressed, now_ms); + } + Ok(Some(buf.len())) + } _ => Err(Error::new(EINVAL)), } } diff --git a/local/recipes/system/redbear-ime/source/src/main.rs b/local/recipes/system/redbear-ime/source/src/main.rs index 87f518043..52fd42d0f 100644 --- a/local/recipes/system/redbear-ime/source/src/main.rs +++ b/local/recipes/system/redbear-ime/source/src/main.rs @@ -1,10 +1,7 @@ mod scheme; -use std::io::Write; -use std::thread; -use std::time::Duration; - use scheme::ImeScheme; +use std::io::Write; fn log_msg(level: &str, msg: &str) { let _ = writeln!(std::io::stderr(), "[ime] {} {}", level, msg); @@ -13,8 +10,7 @@ fn log_msg(level: &str, msg: &str) { fn main() { let mut scheme = ImeScheme::new(); - let socket = redox_scheme::Socket::nonblock("ime") - .expect("ime: failed to register scheme:ime"); + let socket = redox_scheme::Socket::create("ime").expect("ime: failed to register scheme:ime"); log_msg("INFO", "registered scheme:ime"); loop { @@ -25,15 +21,16 @@ fn main() { break; } Err(e) => { - log_msg("WARN", &format!("scheme read error (ignoring): {}", e)); - thread::sleep(Duration::from_millis(100)); - continue; + log_msg("ERROR", &format!("scheme read error: {}", e)); + break; } }; match request.handle_scheme_block_mut(&mut scheme) { Ok(response) => { - if let Err(e) = socket.write_response(response, redox_scheme::SignalBehavior::Restart) { + if let Err(e) = + socket.write_response(response, redox_scheme::SignalBehavior::Restart) + { log_msg("ERROR", &format!("failed to write response: {}", e)); } } diff --git a/local/recipes/system/redbear-ime/source/src/scheme.rs b/local/recipes/system/redbear-ime/source/src/scheme.rs index 2bf991788..05a81cd61 100644 --- a/local/recipes/system/redbear-ime/source/src/scheme.rs +++ b/local/recipes/system/redbear-ime/source/src/scheme.rs @@ -93,17 +93,50 @@ impl BasicLatinEngine { } fn scancode_to_char(scancode: u8, shift: bool) -> Option { let lower = match scancode { - 0x02 => '1', 0x03 => '2', 0x04 => '3', 0x05 => '4', 0x06 => '5', - 0x07 => '6', 0x08 => '7', 0x09 => '8', 0x0A => '9', 0x0B => '0', - 0x10 => 'q', 0x11 => 'w', 0x12 => 'e', 0x13 => 'r', 0x14 => 't', - 0x15 => 'y', 0x16 => 'u', 0x17 => 'i', 0x18 => 'o', 0x19 => 'p', - 0x1E => 'a', 0x1F => 's', 0x20 => 'd', 0x21 => 'f', 0x22 => 'g', - 0x23 => 'h', 0x24 => 'j', 0x25 => 'k', 0x26 => 'l', - 0x2C => 'z', 0x2D => 'x', 0x2E => 'c', 0x2F => 'v', 0x30 => 'b', - 0x31 => 'n', 0x32 => 'm', - 0x27 => ';', 0x28 => '\'', 0x29 => '`', - 0x33 => ',', 0x34 => '.', 0x35 => '/', - 0x0C => '-', 0x0D => '=', + 0x02 => '1', + 0x03 => '2', + 0x04 => '3', + 0x05 => '4', + 0x06 => '5', + 0x07 => '6', + 0x08 => '7', + 0x09 => '8', + 0x0A => '9', + 0x0B => '0', + 0x10 => 'q', + 0x11 => 'w', + 0x12 => 'e', + 0x13 => 'r', + 0x14 => 't', + 0x15 => 'y', + 0x16 => 'u', + 0x17 => 'i', + 0x18 => 'o', + 0x19 => 'p', + 0x1E => 'a', + 0x1F => 's', + 0x20 => 'd', + 0x21 => 'f', + 0x22 => 'g', + 0x23 => 'h', + 0x24 => 'j', + 0x25 => 'k', + 0x26 => 'l', + 0x2C => 'z', + 0x2D => 'x', + 0x2E => 'c', + 0x2F => 'v', + 0x30 => 'b', + 0x31 => 'n', + 0x32 => 'm', + 0x27 => ';', + 0x28 => '\'', + 0x29 => '`', + 0x33 => ',', + 0x34 => '.', + 0x35 => '/', + 0x0C => '-', + 0x0D => '=', _ => return None, }; if shift { @@ -157,8 +190,10 @@ impl ImeEngine for BasicLatinEngine { let is_dead_trigger = scancode == self.compose_scancode; if is_dead_trigger { let dead_keys = [ - ('\u{0300}', "grave"), ('\u{0301}', "acute"), - ('\u{0302}', "circumflex"), ('\u{0308}', "diaeresis"), + ('\u{0300}', "grave"), + ('\u{0301}', "acute"), + ('\u{0302}', "circumflex"), + ('\u{0308}', "diaeresis"), ('\u{030C}', "caron"), ]; let (dk, _name) = dead_keys[self.dead_key_index % dead_keys.len()]; @@ -206,6 +241,9 @@ impl ImeScheme { self.state.composing = false; self.state.preedit.clear(); } + if result.candidates_changed { + self.state.composing = !self.engine.candidates().is_empty(); + } if !result.preedit.is_empty() { self.state.composing = true; self.state.preedit = result.preedit; @@ -235,13 +273,11 @@ impl redox_scheme::SchemeBlockMut for ImeScheme { let content: Vec = match &handle.kind { HandleKind::Root => "state\ncompose\ncandidates\n".as_bytes().to_vec(), - HandleKind::State => { - format!( - "composing={}\npreedit={}\ncommitted={}\n", - self.state.composing, self.state.preedit, self.state.committed - ) - .into_bytes() - } + HandleKind::State => format!( + "composing={}\npreedit={}\ncommitted={}\n", + self.state.composing, self.state.preedit, self.state.committed + ) + .into_bytes(), HandleKind::Compose => "basic-latin\n".as_bytes().to_vec(), HandleKind::Candidates => { let mut out = String::new(); @@ -267,9 +303,34 @@ impl redox_scheme::SchemeBlockMut for ImeScheme { match &handle.kind { HandleKind::Compose => { let input = String::from_utf8_lossy(buf); - if input.trim() == "reset" { - self.engine.reset(); - self.state = InputState::default(); + for line in input.lines() { + let line = line.trim(); + if line == "reset" { + self.engine.reset(); + self.state = InputState::default(); + continue; + } + let mut fields = line.split_whitespace(); + let Some("feed") = fields.next() else { + continue; + }; + let Some(scancode) = fields.next().and_then(|field| field.parse::().ok()) + else { + continue; + }; + let Some(pressed) = fields.next().and_then(|field| field.parse::().ok()) + else { + continue; + }; + let shift = fields + .next() + .and_then(|field| field.parse::().ok()) + .unwrap_or(false); + let altgr = fields + .next() + .and_then(|field| field.parse::().ok()) + .unwrap_or(false); + self.feed(scancode, pressed, shift, altgr); } Ok(Some(buf.len())) } diff --git a/local/recipes/system/redbear-keymapd/source/src/keymap.rs b/local/recipes/system/redbear-keymapd/source/src/keymap.rs index cbe1320f2..9f3aa9503 100644 --- a/local/recipes/system/redbear-keymapd/source/src/keymap.rs +++ b/local/recipes/system/redbear-keymapd/source/src/keymap.rs @@ -79,8 +79,8 @@ impl Keymap { dead_keys: Vec, } - let file: KeymapFile = serde_json::from_str(json_str) - .map_err(|e| format!("parse error: {}", e))?; + let file: KeymapFile = + serde_json::from_str(json_str).map_err(|e| format!("parse error: {}", e))?; let mut map = HashMap::new(); for entry in file.entries { @@ -119,192 +119,609 @@ impl BuiltinKeymaps { fn common_dead_keys() -> Vec { vec![ - DeadKeyEntry { dead_key: '`', base: 'a', composed: 'à' }, - DeadKeyEntry { dead_key: '`', base: 'e', composed: 'è' }, - DeadKeyEntry { dead_key: '`', base: 'i', composed: 'ì' }, - DeadKeyEntry { dead_key: '`', base: 'o', composed: 'ò' }, - DeadKeyEntry { dead_key: '`', base: 'u', composed: 'ù' }, - DeadKeyEntry { dead_key: '`', base: 'A', composed: 'À' }, - DeadKeyEntry { dead_key: '`', base: 'E', composed: 'È' }, - DeadKeyEntry { dead_key: '`', base: 'I', composed: 'Ì' }, - DeadKeyEntry { dead_key: '`', base: 'O', composed: 'Ò' }, - DeadKeyEntry { dead_key: '`', base: 'U', composed: 'Ù' }, - DeadKeyEntry { dead_key: '\'', base: 'a', composed: 'á' }, - DeadKeyEntry { dead_key: '\'', base: 'e', composed: 'é' }, - DeadKeyEntry { dead_key: '\'', base: 'i', composed: 'í' }, - DeadKeyEntry { dead_key: '\'', base: 'o', composed: 'ó' }, - DeadKeyEntry { dead_key: '\'', base: 'u', composed: 'ú' }, - DeadKeyEntry { dead_key: '\'', base: 'A', composed: 'Á' }, - DeadKeyEntry { dead_key: '\'', base: 'E', composed: 'É' }, - DeadKeyEntry { dead_key: '\'', base: 'I', composed: 'Í' }, - DeadKeyEntry { dead_key: '\'', base: 'O', composed: 'Ó' }, - DeadKeyEntry { dead_key: '\'', base: 'U', composed: 'Ú' }, - DeadKeyEntry { dead_key: '^', base: 'a', composed: 'â' }, - DeadKeyEntry { dead_key: '^', base: 'e', composed: 'ê' }, - DeadKeyEntry { dead_key: '^', base: 'i', composed: 'î' }, - DeadKeyEntry { dead_key: '^', base: 'o', composed: 'ô' }, - DeadKeyEntry { dead_key: '^', base: 'u', composed: 'û' }, - DeadKeyEntry { dead_key: '^', base: 'A', composed: 'Â' }, - DeadKeyEntry { dead_key: '^', base: 'E', composed: 'Ê' }, - DeadKeyEntry { dead_key: '^', base: 'I', composed: 'Î' }, - DeadKeyEntry { dead_key: '^', base: 'O', composed: 'Ô' }, - DeadKeyEntry { dead_key: '^', base: 'U', composed: 'Û' }, - DeadKeyEntry { dead_key: '"', base: 'a', composed: 'ä' }, - DeadKeyEntry { dead_key: '"', base: 'e', composed: 'ë' }, - DeadKeyEntry { dead_key: '"', base: 'i', composed: 'ï' }, - DeadKeyEntry { dead_key: '"', base: 'o', composed: 'ö' }, - DeadKeyEntry { dead_key: '"', base: 'u', composed: 'ü' }, - DeadKeyEntry { dead_key: '"', base: 'A', composed: 'Ä' }, - DeadKeyEntry { dead_key: '"', base: 'E', composed: 'Ë' }, - DeadKeyEntry { dead_key: '"', base: 'I', composed: 'Ï' }, - DeadKeyEntry { dead_key: '"', base: 'O', composed: 'Ö' }, - DeadKeyEntry { dead_key: '"', base: 'U', composed: 'Ü' }, - DeadKeyEntry { dead_key: '~', base: 'a', composed: 'ã' }, - DeadKeyEntry { dead_key: '~', base: 'n', composed: 'ñ' }, - DeadKeyEntry { dead_key: '~', base: 'o', composed: 'õ' }, - DeadKeyEntry { dead_key: '~', base: 'A', composed: 'Ã' }, - DeadKeyEntry { dead_key: '~', base: 'N', composed: 'Ñ' }, - DeadKeyEntry { dead_key: '~', base: 'O', composed: 'Õ' }, + DeadKeyEntry { + dead_key: '`', + base: 'a', + composed: 'à', + }, + DeadKeyEntry { + dead_key: '`', + base: 'e', + composed: 'è', + }, + DeadKeyEntry { + dead_key: '`', + base: 'i', + composed: 'ì', + }, + DeadKeyEntry { + dead_key: '`', + base: 'o', + composed: 'ò', + }, + DeadKeyEntry { + dead_key: '`', + base: 'u', + composed: 'ù', + }, + DeadKeyEntry { + dead_key: '`', + base: 'A', + composed: 'À', + }, + DeadKeyEntry { + dead_key: '`', + base: 'E', + composed: 'È', + }, + DeadKeyEntry { + dead_key: '`', + base: 'I', + composed: 'Ì', + }, + DeadKeyEntry { + dead_key: '`', + base: 'O', + composed: 'Ò', + }, + DeadKeyEntry { + dead_key: '`', + base: 'U', + composed: 'Ù', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'a', + composed: 'á', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'e', + composed: 'é', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'i', + composed: 'í', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'o', + composed: 'ó', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'u', + composed: 'ú', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'A', + composed: 'Á', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'E', + composed: 'É', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'I', + composed: 'Í', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'O', + composed: 'Ó', + }, + DeadKeyEntry { + dead_key: '\'', + base: 'U', + composed: 'Ú', + }, + DeadKeyEntry { + dead_key: '^', + base: 'a', + composed: 'â', + }, + DeadKeyEntry { + dead_key: '^', + base: 'e', + composed: 'ê', + }, + DeadKeyEntry { + dead_key: '^', + base: 'i', + composed: 'î', + }, + DeadKeyEntry { + dead_key: '^', + base: 'o', + composed: 'ô', + }, + DeadKeyEntry { + dead_key: '^', + base: 'u', + composed: 'û', + }, + DeadKeyEntry { + dead_key: '^', + base: 'A', + composed: 'Â', + }, + DeadKeyEntry { + dead_key: '^', + base: 'E', + composed: 'Ê', + }, + DeadKeyEntry { + dead_key: '^', + base: 'I', + composed: 'Î', + }, + DeadKeyEntry { + dead_key: '^', + base: 'O', + composed: 'Ô', + }, + DeadKeyEntry { + dead_key: '^', + base: 'U', + composed: 'Û', + }, + DeadKeyEntry { + dead_key: '"', + base: 'a', + composed: 'ä', + }, + DeadKeyEntry { + dead_key: '"', + base: 'e', + composed: 'ë', + }, + DeadKeyEntry { + dead_key: '"', + base: 'i', + composed: 'ï', + }, + DeadKeyEntry { + dead_key: '"', + base: 'o', + composed: 'ö', + }, + DeadKeyEntry { + dead_key: '"', + base: 'u', + composed: 'ü', + }, + DeadKeyEntry { + dead_key: '"', + base: 'A', + composed: 'Ä', + }, + DeadKeyEntry { + dead_key: '"', + base: 'E', + composed: 'Ë', + }, + DeadKeyEntry { + dead_key: '"', + base: 'I', + composed: 'Ï', + }, + DeadKeyEntry { + dead_key: '"', + base: 'O', + composed: 'Ö', + }, + DeadKeyEntry { + dead_key: '"', + base: 'U', + composed: 'Ü', + }, + DeadKeyEntry { + dead_key: '~', + base: 'a', + composed: 'ã', + }, + DeadKeyEntry { + dead_key: '~', + base: 'n', + composed: 'ñ', + }, + DeadKeyEntry { + dead_key: '~', + base: 'o', + composed: 'õ', + }, + DeadKeyEntry { + dead_key: '~', + base: 'A', + composed: 'Ã', + }, + DeadKeyEntry { + dead_key: '~', + base: 'N', + composed: 'Ñ', + }, + DeadKeyEntry { + dead_key: '~', + base: 'O', + composed: 'Õ', + }, ] } fn make_us() -> Keymap { let mut entries = HashMap::new(); let bindings: &[(u8, char, char)] = &[ - (0x01, '\x1B', '\x1B'), (0x02, '1', '!'), (0x03, '2', '@'), - (0x04, '3', '#'), (0x05, '4', '$'), (0x06, '5', '%'), - (0x07, '6', '^'), (0x08, '7', '&'), (0x09, '8', '*'), - (0x0A, '9', '('), (0x0B, '0', ')'), (0x0C, '-', '_'), - (0x0D, '=', '+'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'), - (0x10, 'q', 'Q'), (0x11, 'w', 'W'), (0x12, 'e', 'E'), - (0x13, 'r', 'R'), (0x14, 't', 'T'), (0x15, 'y', 'Y'), - (0x16, 'u', 'U'), (0x17, 'i', 'I'), (0x18, 'o', 'O'), - (0x19, 'p', 'P'), (0x1A, '[', '{'), (0x1B, ']', '}'), - (0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 's', 'S'), - (0x20, 'd', 'D'), (0x21, 'f', 'F'), (0x22, 'g', 'G'), - (0x23, 'h', 'H'), (0x24, 'j', 'J'), (0x25, 'k', 'K'), - (0x26, 'l', 'L'), (0x27, ';', ':'), (0x28, '\'', '"'), - (0x29, '`', '~'), (0x2B, '\\', '|'), (0x2C, 'z', 'Z'), - (0x2D, 'x', 'X'), (0x2E, 'c', 'C'), (0x2F, 'v', 'V'), - (0x30, 'b', 'B'), (0x31, 'n', 'N'), (0x32, 'm', 'M'), - (0x33, ',', '<'), (0x34, '.', '>'), (0x35, '/', '?'), + (0x01, '\x1B', '\x1B'), + (0x02, '1', '!'), + (0x03, '2', '@'), + (0x04, '3', '#'), + (0x05, '4', '$'), + (0x06, '5', '%'), + (0x07, '6', '^'), + (0x08, '7', '&'), + (0x09, '8', '*'), + (0x0A, '9', '('), + (0x0B, '0', ')'), + (0x0C, '-', '_'), + (0x0D, '=', '+'), + (0x0E, '\x7F', '\x7F'), + (0x0F, '\t', '\t'), + (0x10, 'q', 'Q'), + (0x11, 'w', 'W'), + (0x12, 'e', 'E'), + (0x13, 'r', 'R'), + (0x14, 't', 'T'), + (0x15, 'y', 'Y'), + (0x16, 'u', 'U'), + (0x17, 'i', 'I'), + (0x18, 'o', 'O'), + (0x19, 'p', 'P'), + (0x1A, '[', '{'), + (0x1B, ']', '}'), + (0x1C, '\n', '\n'), + (0x1E, 'a', 'A'), + (0x1F, 's', 'S'), + (0x20, 'd', 'D'), + (0x21, 'f', 'F'), + (0x22, 'g', 'G'), + (0x23, 'h', 'H'), + (0x24, 'j', 'J'), + (0x25, 'k', 'K'), + (0x26, 'l', 'L'), + (0x27, ';', ':'), + (0x28, '\'', '"'), + (0x29, '`', '~'), + (0x2B, '\\', '|'), + (0x2C, 'z', 'Z'), + (0x2D, 'x', 'X'), + (0x2E, 'c', 'C'), + (0x2F, 'v', 'V'), + (0x30, 'b', 'B'), + (0x31, 'n', 'N'), + (0x32, 'm', 'M'), + (0x33, ',', '<'), + (0x34, '.', '>'), + (0x35, '/', '?'), (0x39, ' ', ' '), ]; for &(sc, normal, shifted) in bindings { - entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' }); + entries.insert( + sc, + KeymapEntry { + scancode: sc, + normal, + shifted, + altgr: '\0', + altgr_shifted: '\0', + }, + ); } let dead_keys = Self::common_dead_keys(); - Keymap { name: "us".into(), entries, compose: Vec::new(), dead_keys } + Keymap { + name: "us".into(), + entries, + compose: Vec::new(), + dead_keys, + } } fn make_gb() -> Keymap { let mut km = Self::make_us(); km.name = "gb".into(); - if let Some(e) = km.entries.get_mut(&0x29) { e.shifted = '¬'; } - if let Some(e) = km.entries.get_mut(&0x2B) { e.normal = '#'; e.shifted = '~'; } + if let Some(e) = km.entries.get_mut(&0x29) { + e.shifted = '¬'; + } + if let Some(e) = km.entries.get_mut(&0x2B) { + e.normal = '#'; + e.shifted = '~'; + } km } fn make_dvorak() -> Keymap { let mut entries = HashMap::new(); let remap: &[(u8, char, char)] = &[ - (0x01, '\x1B', '\x1B'), (0x02, '1', '!'), (0x03, '2', '@'), - (0x04, '3', '#'), (0x05, '4', '$'), (0x06, '5', '%'), - (0x07, '6', '^'), (0x08, '7', '&'), (0x09, '8', '*'), - (0x0A, '9', '('), (0x0B, '0', ')'), (0x0C, '[', '{'), - (0x0D, ']', '}'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'), - (0x10, '\'', '"'), (0x11, ',', '<'), (0x12, '.', '>'), - (0x13, 'p', 'P'), (0x14, 'y', 'Y'), (0x15, 'f', 'F'), - (0x16, 'g', 'G'), (0x17, 'c', 'C'), (0x18, 'r', 'R'), - (0x19, 'l', 'L'), (0x1A, '/', '?'), (0x1B, '=', '+'), - (0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 'o', 'O'), - (0x20, 'e', 'E'), (0x21, 'u', 'U'), (0x22, 'i', 'I'), - (0x23, 'd', 'D'), (0x24, 'h', 'H'), (0x25, 't', 'T'), - (0x26, 'n', 'N'), (0x27, 's', 'S'), (0x28, '-', '_'), - (0x29, '`', '~'), (0x2B, '\\', '|'), (0x2C, ';', ':'), - (0x2D, 'q', 'Q'), (0x2E, 'j', 'J'), (0x2F, 'k', 'K'), - (0x30, 'x', 'X'), (0x31, 'b', 'B'), (0x32, 'm', 'M'), - (0x33, 'w', 'W'), (0x34, 'v', 'V'), (0x35, 'z', 'Z'), + (0x01, '\x1B', '\x1B'), + (0x02, '1', '!'), + (0x03, '2', '@'), + (0x04, '3', '#'), + (0x05, '4', '$'), + (0x06, '5', '%'), + (0x07, '6', '^'), + (0x08, '7', '&'), + (0x09, '8', '*'), + (0x0A, '9', '('), + (0x0B, '0', ')'), + (0x0C, '[', '{'), + (0x0D, ']', '}'), + (0x0E, '\x7F', '\x7F'), + (0x0F, '\t', '\t'), + (0x10, '\'', '"'), + (0x11, ',', '<'), + (0x12, '.', '>'), + (0x13, 'p', 'P'), + (0x14, 'y', 'Y'), + (0x15, 'f', 'F'), + (0x16, 'g', 'G'), + (0x17, 'c', 'C'), + (0x18, 'r', 'R'), + (0x19, 'l', 'L'), + (0x1A, '/', '?'), + (0x1B, '=', '+'), + (0x1C, '\n', '\n'), + (0x1E, 'a', 'A'), + (0x1F, 'o', 'O'), + (0x20, 'e', 'E'), + (0x21, 'u', 'U'), + (0x22, 'i', 'I'), + (0x23, 'd', 'D'), + (0x24, 'h', 'H'), + (0x25, 't', 'T'), + (0x26, 'n', 'N'), + (0x27, 's', 'S'), + (0x28, '-', '_'), + (0x29, '`', '~'), + (0x2B, '\\', '|'), + (0x2C, ';', ':'), + (0x2D, 'q', 'Q'), + (0x2E, 'j', 'J'), + (0x2F, 'k', 'K'), + (0x30, 'x', 'X'), + (0x31, 'b', 'B'), + (0x32, 'm', 'M'), + (0x33, 'w', 'W'), + (0x34, 'v', 'V'), + (0x35, 'z', 'Z'), (0x39, ' ', ' '), ]; for &(sc, normal, shifted) in remap { - entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' }); + entries.insert( + sc, + KeymapEntry { + scancode: sc, + normal, + shifted, + altgr: '\0', + altgr_shifted: '\0', + }, + ); + } + Keymap { + name: "dvorak".into(), + entries, + compose: Vec::new(), + dead_keys: Self::common_dead_keys(), } - Keymap { name: "dvorak".into(), entries, compose: Vec::new(), dead_keys: Self::common_dead_keys() } } fn make_azerty() -> Keymap { let mut entries = HashMap::new(); let bindings: &[(u8, char, char)] = &[ - (0x01, '\x1B', '\x1B'), (0x02, '&', '1'), (0x03, 'é', '2'), - (0x04, '"', '3'), (0x05, '\'', '4'), (0x06, '(', '5'), - (0x07, '-', '6'), (0x08, 'è', '7'), (0x09, '_', '8'), - (0x0A, 'ç', '9'), (0x0B, 'à', '0'), (0x0C, ')', '°'), - (0x0D, '=', '+'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'), - (0x10, 'a', 'A'), (0x11, 'z', 'Z'), (0x12, 'e', 'E'), - (0x13, 'r', 'R'), (0x14, 't', 'T'), (0x15, 'y', 'Y'), - (0x16, 'u', 'U'), (0x17, 'i', 'I'), (0x18, 'o', 'O'), - (0x19, 'p', 'P'), (0x1A, '^', '¨'), (0x1B, '$', '£'), - (0x1C, '\n', '\n'), (0x1E, 'q', 'Q'), (0x1F, 's', 'S'), - (0x20, 'd', 'D'), (0x21, 'f', 'F'), (0x22, 'g', 'G'), - (0x23, 'h', 'H'), (0x24, 'j', 'J'), (0x25, 'k', 'K'), - (0x26, 'l', 'L'), (0x27, 'm', 'M'), (0x28, 'ù', '%'), - (0x29, '²', '~'), (0x2B, '*', 'µ'), (0x2C, 'w', 'W'), - (0x2D, 'x', 'X'), (0x2E, 'c', 'C'), (0x2F, 'v', 'V'), - (0x30, 'b', 'B'), (0x31, 'n', 'N'), (0x32, ',', '?'), - (0x33, ';', '.'), (0x34, ':', '/'), (0x35, '!', '§'), + (0x01, '\x1B', '\x1B'), + (0x02, '&', '1'), + (0x03, 'é', '2'), + (0x04, '"', '3'), + (0x05, '\'', '4'), + (0x06, '(', '5'), + (0x07, '-', '6'), + (0x08, 'è', '7'), + (0x09, '_', '8'), + (0x0A, 'ç', '9'), + (0x0B, 'à', '0'), + (0x0C, ')', '°'), + (0x0D, '=', '+'), + (0x0E, '\x7F', '\x7F'), + (0x0F, '\t', '\t'), + (0x10, 'a', 'A'), + (0x11, 'z', 'Z'), + (0x12, 'e', 'E'), + (0x13, 'r', 'R'), + (0x14, 't', 'T'), + (0x15, 'y', 'Y'), + (0x16, 'u', 'U'), + (0x17, 'i', 'I'), + (0x18, 'o', 'O'), + (0x19, 'p', 'P'), + (0x1A, '^', '¨'), + (0x1B, '$', '£'), + (0x1C, '\n', '\n'), + (0x1E, 'q', 'Q'), + (0x1F, 's', 'S'), + (0x20, 'd', 'D'), + (0x21, 'f', 'F'), + (0x22, 'g', 'G'), + (0x23, 'h', 'H'), + (0x24, 'j', 'J'), + (0x25, 'k', 'K'), + (0x26, 'l', 'L'), + (0x27, 'm', 'M'), + (0x28, 'ù', '%'), + (0x29, '²', '~'), + (0x2B, '*', 'µ'), + (0x2C, 'w', 'W'), + (0x2D, 'x', 'X'), + (0x2E, 'c', 'C'), + (0x2F, 'v', 'V'), + (0x30, 'b', 'B'), + (0x31, 'n', 'N'), + (0x32, ',', '?'), + (0x33, ';', '.'), + (0x34, ':', '/'), + (0x35, '!', '§'), (0x39, ' ', ' '), ]; for &(sc, normal, shifted) in bindings { - entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' }); + entries.insert( + sc, + KeymapEntry { + scancode: sc, + normal, + shifted, + altgr: '\0', + altgr_shifted: '\0', + }, + ); } let mut dead_keys = Self::common_dead_keys(); - dead_keys.push(DeadKeyEntry { dead_key: '^', base: ' ', composed: '^' }); - dead_keys.push(DeadKeyEntry { dead_key: '¨', base: ' ', composed: '¨' }); - Keymap { name: "azerty".into(), entries, compose: Vec::new(), dead_keys } + dead_keys.push(DeadKeyEntry { + dead_key: '^', + base: ' ', + composed: '^', + }); + dead_keys.push(DeadKeyEntry { + dead_key: '¨', + base: ' ', + composed: '¨', + }); + Keymap { + name: "azerty".into(), + entries, + compose: Vec::new(), + dead_keys, + } } fn make_bepo() -> Keymap { let mut entries = HashMap::new(); let bindings: &[(u8, char, char)] = &[ - (0x01, '\x1B', '\x1B'), (0x02, '"', '1'), (0x03, '«', '2'), - (0x04, '»', '3'), (0x05, '(', '4'), (0x06, ')', '5'), - (0x07, '@', '6'), (0x08, '+', '7'), (0x09, '-', '8'), - (0x0A, '/', '9'), (0x0B, '*', '0'), (0x0C, '=', '°'), - (0x0D, '%', '`'), (0x0E, '\x7F', '\x7F'), (0x0F, '\t', '\t'), - (0x10, 'b', 'B'), (0x11, 'é', 'É'), (0x12, 'p', 'P'), - (0x13, 'o', 'O'), (0x14, 'è', 'È'), (0x15, '^', '!'), - (0x16, 'v', 'V'), (0x17, 'd', 'D'), (0x18, 'l', 'L'), - (0x19, 'j', 'J'), (0x1A, 'z', 'Z'), (0x1B, 'w', 'W'), - (0x1C, '\n', '\n'), (0x1E, 'a', 'A'), (0x1F, 'u', 'U'), - (0x20, 'i', 'I'), (0x21, 'e', 'E'), (0x22, ',', ';'), - (0x23, 'c', 'C'), (0x24, 't', 'T'), (0x25, 's', 'S'), - (0x26, 'r', 'R'), (0x27, 'n', 'N'), (0x28, 'm', 'M'), - (0x29, 'ç', 'Ç'), (0x2B, '\'', '?'), (0x2C, 'x', 'X'), - (0x2D, 'f', 'F'), (0x2E, 'h', 'H'), (0x2F, 'q', 'Q'), - (0x30, 'g', 'G'), (0x31, 'y', 'Y'), (0x32, 'k', 'K'), - (0x33, '.', ':'), (0x34, '/', '§'), (0x35, 'g', 'G'), + (0x01, '\x1B', '\x1B'), + (0x02, '"', '1'), + (0x03, '«', '2'), + (0x04, '»', '3'), + (0x05, '(', '4'), + (0x06, ')', '5'), + (0x07, '@', '6'), + (0x08, '+', '7'), + (0x09, '-', '8'), + (0x0A, '/', '9'), + (0x0B, '*', '0'), + (0x0C, '=', '°'), + (0x0D, '%', '`'), + (0x0E, '\x7F', '\x7F'), + (0x0F, '\t', '\t'), + (0x10, 'b', 'B'), + (0x11, 'é', 'É'), + (0x12, 'p', 'P'), + (0x13, 'o', 'O'), + (0x14, 'è', 'È'), + (0x15, '^', '!'), + (0x16, 'v', 'V'), + (0x17, 'd', 'D'), + (0x18, 'l', 'L'), + (0x19, 'j', 'J'), + (0x1A, 'z', 'Z'), + (0x1B, 'w', 'W'), + (0x1C, '\n', '\n'), + (0x1E, 'a', 'A'), + (0x1F, 'u', 'U'), + (0x20, 'i', 'I'), + (0x21, 'e', 'E'), + (0x22, ',', ';'), + (0x23, 'c', 'C'), + (0x24, 't', 'T'), + (0x25, 's', 'S'), + (0x26, 'r', 'R'), + (0x27, 'n', 'N'), + (0x28, 'm', 'M'), + (0x29, 'ç', 'Ç'), + (0x2B, '\'', '?'), + (0x2C, 'x', 'X'), + (0x2D, 'f', 'F'), + (0x2E, 'h', 'H'), + (0x2F, 'q', 'Q'), + (0x30, 'g', 'G'), + (0x31, 'y', 'Y'), + (0x32, 'k', 'K'), + (0x33, '.', ':'), + (0x34, '/', '§'), + (0x35, 'g', 'G'), (0x39, ' ', ' '), ]; for &(sc, normal, shifted) in bindings { - entries.insert(sc, KeymapEntry { scancode: sc, normal, shifted, altgr: '\0', altgr_shifted: '\0' }); + entries.insert( + sc, + KeymapEntry { + scancode: sc, + normal, + shifted, + altgr: '\0', + altgr_shifted: '\0', + }, + ); } let mut dead_keys = Self::common_dead_keys(); - dead_keys.push(DeadKeyEntry { dead_key: '^', base: ' ', composed: '^' }); - Keymap { name: "bepo".into(), entries, compose: Vec::new(), dead_keys } + dead_keys.push(DeadKeyEntry { + dead_key: '^', + base: ' ', + composed: '^', + }); + Keymap { + name: "bepo".into(), + entries, + compose: Vec::new(), + dead_keys, + } } fn make_it() -> Keymap { let mut km = Self::make_us(); km.name = "it".into(); - if let Some(e) = km.entries.get_mut(&0x29) { e.normal = '\\'; e.shifted = '|'; } - if let Some(e) = km.entries.get_mut(&0x0C) { e.normal = '\''; e.shifted = '?'; } - if let Some(e) = km.entries.get_mut(&0x0D) { e.normal = 'ì'; e.shifted = '^'; } - if let Some(e) = km.entries.get_mut(&0x1A) { e.normal = 'è'; e.shifted = 'é'; } - if let Some(e) = km.entries.get_mut(&0x1B) { e.normal = '+'; e.shifted = '*'; } - if let Some(e) = km.entries.get_mut(&0x27) { e.normal = 'ò'; e.shifted = 'ç'; } - if let Some(e) = km.entries.get_mut(&0x28) { e.normal = 'à'; e.shifted = '°'; } - if let Some(e) = km.entries.get_mut(&0x2B) { e.normal = 'ù'; e.shifted = '§'; } + if let Some(e) = km.entries.get_mut(&0x29) { + e.normal = '\\'; + e.shifted = '|'; + } + if let Some(e) = km.entries.get_mut(&0x0C) { + e.normal = '\''; + e.shifted = '?'; + } + if let Some(e) = km.entries.get_mut(&0x0D) { + e.normal = 'ì'; + e.shifted = '^'; + } + if let Some(e) = km.entries.get_mut(&0x1A) { + e.normal = 'è'; + e.shifted = 'é'; + } + if let Some(e) = km.entries.get_mut(&0x1B) { + e.normal = '+'; + e.shifted = '*'; + } + if let Some(e) = km.entries.get_mut(&0x27) { + e.normal = 'ò'; + e.shifted = 'ç'; + } + if let Some(e) = km.entries.get_mut(&0x28) { + e.normal = 'à'; + e.shifted = '°'; + } + if let Some(e) = km.entries.get_mut(&0x2B) { + e.normal = 'ù'; + e.shifted = '§'; + } km } } diff --git a/local/recipes/system/redbear-keymapd/source/src/main.rs b/local/recipes/system/redbear-keymapd/source/src/main.rs index 4b1efd8c4..4fd91f0b7 100644 --- a/local/recipes/system/redbear-keymapd/source/src/main.rs +++ b/local/recipes/system/redbear-keymapd/source/src/main.rs @@ -4,8 +4,6 @@ mod xkb; use std::env; use std::io::Write; -use std::thread; -use std::time::Duration; use scheme::KeymapScheme; @@ -24,13 +22,33 @@ fn main() { Err(_) => "/etc/keymaps".to_string(), }; if let Err(e) = scheme.load_from_dir(&keymap_dir) { - log_msg("ERROR", &format!("failed to load keymaps from {}: {}", keymap_dir, e)); + log_msg( + "ERROR", + &format!("failed to load keymaps from {}: {}", keymap_dir, e), + ); } - log_msg("INFO", &format!("loaded {} keymap(s)", scheme.keymap_count())); + if let Ok(xkb_root) = env::var("XKB_CONFIG_ROOT") { + let layout = env::var("XKB_DEFAULT_LAYOUT").unwrap_or_else(|_| "us".to_string()); + let variant = env::var("XKB_DEFAULT_VARIANT").ok(); + if let Err(e) = scheme.load_xkb(&xkb_root, &layout, variant.as_deref()) { + log_msg( + "ERROR", + &format!( + "failed to load XKB keymap {} from {}: {}", + layout, xkb_root, e + ), + ); + } + } - let socket = redox_scheme::Socket::nonblock("keymap") - .expect("keymapd: failed to register scheme:keymap"); + log_msg( + "INFO", + &format!("loaded {} keymap(s)", scheme.keymap_count()), + ); + + let socket = + redox_scheme::Socket::create("keymap").expect("keymapd: failed to register scheme:keymap"); log_msg("INFO", "registered scheme:keymap"); loop { @@ -41,15 +59,16 @@ fn main() { break; } Err(e) => { - log_msg("WARN", &format!("scheme read error (ignoring): {}", e)); - thread::sleep(Duration::from_millis(100)); - continue; + log_msg("ERROR", &format!("scheme read error: {}", e)); + break; } }; match request.handle_scheme_block_mut(&mut scheme) { Ok(response) => { - if let Err(e) = socket.write_response(response, redox_scheme::SignalBehavior::Restart) { + if let Err(e) = + socket.write_response(response, redox_scheme::SignalBehavior::Restart) + { log_msg("ERROR", &format!("failed to write response: {}", e)); } } diff --git a/local/recipes/system/redbear-keymapd/source/src/scheme.rs b/local/recipes/system/redbear-keymapd/source/src/scheme.rs index fa4b50d55..f1f077a63 100644 --- a/local/recipes/system/redbear-keymapd/source/src/scheme.rs +++ b/local/recipes/system/redbear-keymapd/source/src/scheme.rs @@ -7,6 +7,7 @@ use syscall::flag::{MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; use crate::keymap::Keymap; +#[derive(Clone)] enum HandleKind { Root, Active, @@ -69,7 +70,12 @@ impl KeymapScheme { Ok(()) } - pub fn load_xkb(&mut self, xkb_dir: &str, layout: &str, variant: Option<&str>) -> io::Result<()> { + pub fn load_xkb( + &mut self, + xkb_dir: &str, + layout: &str, + variant: Option<&str>, + ) -> io::Result<()> { let km = crate::xkb::load_xkb_keymap(xkb_dir, layout, variant) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let name = match variant { @@ -136,9 +142,12 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme { } fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let (kind, offset) = { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + (handle.kind.clone(), handle.offset) + }; - let content: Vec = match &handle.kind { + let content: Vec = match &kind { HandleKind::Root => { let mut listing = String::new(); listing.push_str("active\nlist\n"); @@ -147,7 +156,12 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme { } listing.into_bytes() } - HandleKind::Active => self.active_keymap.clone().into_bytes(), + HandleKind::Active => format!( + "name={}\nsample_scancode_30={}\n", + self.active_keymap, + self.translate(0x1E, false, false), + ) + .into_bytes(), HandleKind::List => { let mut listing = String::new(); for (i, name) in self.keymaps.keys().enumerate() { @@ -162,20 +176,25 @@ impl redox_scheme::SchemeBlockMut for KeymapScheme { HandleKind::Keymap { name } => { let km = self.keymaps.get(name).ok_or(Error::new(ENOENT))?; format!( - "name={}\nentries={}\ncompose={}\ndead_keys={}\n", + "name={}\nentries={}\ncompose={}\ndead_keys={}\nsample_scancode_30={}\nsample_altgr_scancode_30={}\nsample_compose_a_acute={}\nsample_compose_sequence={}\n", km.name, km.entries.len(), km.compose.len(), - km.dead_keys.len() + km.dead_keys.len(), + km.get_char(0x1E, false, false), + km.get_char(0x1E, false, true), + km.compose('a', '\''), + km.lookup_compose("compose").unwrap_or('\0'), ) .into_bytes() } }; - if handle.offset >= content.len() { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if offset >= content.len() { return Ok(Some(0)); } - let remaining = &content[handle.offset..]; + let remaining = &content[offset..]; let to_copy = remaining.len().min(buf.len()); buf[..to_copy].copy_from_slice(&remaining[..to_copy]); handle.offset += to_copy; diff --git a/local/recipes/system/redbear-keymapd/source/src/xkb.rs b/local/recipes/system/redbear-keymapd/source/src/xkb.rs index 576f817e9..1f7b84f42 100644 --- a/local/recipes/system/redbear-keymapd/source/src/xkb.rs +++ b/local/recipes/system/redbear-keymapd/source/src/xkb.rs @@ -210,10 +210,22 @@ struct XkbKeyEntry { } fn parse_keysyms(syms: &[&str]) -> (char, char, char, char) { - let normal = syms.get(0).map(|s| keysym_to_char(s.trim())).unwrap_or('\0'); - let shifted = syms.get(1).map(|s| keysym_to_char(s.trim())).unwrap_or('\0'); - let altgr = syms.get(2).map(|s| keysym_to_char(s.trim())).unwrap_or('\0'); - let altgr_shifted = syms.get(3).map(|s| keysym_to_char(s.trim())).unwrap_or('\0'); + let normal = syms + .get(0) + .map(|s| keysym_to_char(s.trim())) + .unwrap_or('\0'); + let shifted = syms + .get(1) + .map(|s| keysym_to_char(s.trim())) + .unwrap_or('\0'); + let altgr = syms + .get(2) + .map(|s| keysym_to_char(s.trim())) + .unwrap_or('\0'); + let altgr_shifted = syms + .get(3) + .map(|s| keysym_to_char(s.trim())) + .unwrap_or('\0'); (normal, shifted, altgr, altgr_shifted) } @@ -242,15 +254,15 @@ pub fn parse_xkb_symbols(content: &str, variant: Option<&str>) -> Result) -> Result Option { }) } -pub fn load_xkb_keymap(xkb_dir: &str, layout: &str, variant: Option<&str>) -> Result { +pub fn load_xkb_keymap( + xkb_dir: &str, + layout: &str, + variant: Option<&str>, +) -> Result { let file_path = format!("{}/symbols/{}", xkb_dir, layout); let content = std::fs::read_to_string(&file_path) .map_err(|e| format!("failed to read XKB symbols file '{}': {}", file_path, e))?; diff --git a/recipes/core/base/daemon/src/lib.rs b/recipes/core/base/daemon/src/lib.rs index 9f5072214..a16d34dc3 100644 --- a/recipes/core/base/daemon/src/lib.rs +++ b/recipes/core/base/daemon/src/lib.rs @@ -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 { + 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, } 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, } 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(()) } diff --git a/recipes/core/base/recipe.toml b/recipes/core/base/recipe.toml index 4ddcee733..9415462ad 100644 --- a/recipes/core/base/recipe.toml +++ b/recipes/core/base/recipe.toml @@ -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 diff --git a/recipes/core/relibc/recipe.toml b/recipes/core/relibc/recipe.toml index d8eb912a4..ddf19d19a 100644 --- a/recipes/core/relibc/recipe.toml +++ b/recipes/core/relibc/recipe.toml @@ -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",