From 706050482b03ded06c4be74f536116a7b10666de Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Fri, 29 May 2026 21:48:51 +0300 Subject: [PATCH] fix: rewrite coretempd to use redox_scheme Socket + SchemeSync Replaced broken UnixListener::bind(':coretemp') with proper redox_scheme::Socket::create() + SchemeSync trait impl. Event loop uses next_request/handle_sync/write_response pattern. Verified: registers scheme:coretemp, detects CPU info, zero panics. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../system/coretempd/source/Cargo.toml | 1 + .../system/coretempd/source/src/main.rs | 332 ++++++++++++++---- 2 files changed, 264 insertions(+), 69 deletions(-) diff --git a/local/recipes/system/coretempd/source/Cargo.toml b/local/recipes/system/coretempd/source/Cargo.toml index fc5ba10858..1088f446f4 100644 --- a/local/recipes/system/coretempd/source/Cargo.toml +++ b/local/recipes/system/coretempd/source/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] redox-scheme = "0.11" redox_syscall = "0.7" +libredox = "0.1" libc = "0.2" [profile.release] diff --git a/local/recipes/system/coretempd/source/src/main.rs b/local/recipes/system/coretempd/source/src/main.rs index 064aec0c1e..7dda64ea2f 100644 --- a/local/recipes/system/coretempd/source/src/main.rs +++ b/local/recipes/system/coretempd/source/src/main.rs @@ -1,10 +1,17 @@ +use std::env; use std::fs; -use std::io::{Read, Write}; -use std::os::unix::net::UnixListener; use std::thread; use std::time::Duration; +use redox_scheme::scheme::SchemeSync; +use redox_scheme::scheme::SchemeState; +use redox_scheme::{CallerCtx, OpenResult, SignalBehavior, Socket}; +use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT}; +use syscall::flag::O_ACCMODE; +use syscall::schemev2::NewFdFlags; + const POLL_MS: u64 = 2000; +const SCHEME_ROOT_ID: usize = 0; const IA32_THERM_STATUS: u32 = 0x19c; const IA32_TEMPERATURE_TARGET: u32 = 0x1a2; @@ -19,7 +26,8 @@ enum Vendor { fn read_msr(cpu: u32, msr: u32) -> Option { let path = format!("/scheme/sys/msr/{}/{:x}", cpu, msr); - fs::read_to_string(&path).ok() + fs::read_to_string(&path) + .ok() .and_then(|s| u64::from_str_radix(s.trim(), 16).ok()) } @@ -39,27 +47,34 @@ fn detect_cpus() -> Vec { for l in d.lines() { if let Some(id_str) = l.strip_prefix("CPU ") { if let Some((num, _)) = id_str.split_once(':') { - if let Ok(id) = num.trim().parse() { v.push(id); } + if let Ok(id) = num.trim().parse::() { + v.push(id); + } } } } } - if v.is_empty() { v.push(0); } + if v.is_empty() { + v.push(0); + } v } fn read_temperature_intel(cpu: u32, tjmax: u8) -> Option { let raw = read_msr(cpu, IA32_THERM_STATUS)?; let digital_readout = ((raw >> 16) & 0x7F) as u8; - if digital_readout == 0 { return None; } - let temp = tjmax.saturating_sub(digital_readout); - Some(temp as i16) + if digital_readout == 0 { + return None; + } + Some(tjmax.saturating_sub(digital_readout) as i16) } fn read_tjmax_intel(cpu: u32) -> u8 { if let Some(raw) = read_msr(cpu, IA32_TEMPERATURE_TARGET) { let tj = ((raw >> 16) & 0xFF) as u8; - if tj > 0 && tj < 150 { return tj; } + if tj > 0 && tj < 150 { + return tj; + } } 100 } @@ -67,9 +82,10 @@ fn read_tjmax_intel(cpu: u32) -> u8 { fn read_temperature_amd(cpu: u32) -> Option { let raw = read_msr(cpu, AMD_TCTL)?; let tctl = ((raw >> 21) & 0x3FF) as u16; - if tctl == 0 { return None; } - let temp = (tctl as f32) / 8.0; - Some(temp as i16) + if tctl == 0 { + return None; + } + Some(((tctl as f32) / 8.0) as i16) } #[derive(Clone)] @@ -79,78 +95,256 @@ struct CpuInfo { tjmax: u8, } -fn main() { - let scheme_path = ":coretemp"; - let _ = fs::remove_file(scheme_path); - let listener = UnixListener::bind(scheme_path).expect("bind scheme"); - eprintln!("[INFO] coretempd: starting"); +#[derive(Clone)] +enum Handle { + Listing, + CpuTemp { cpu_id: u32 }, +} - let cpus = detect_cpus(); - eprintln!("[INFO] coretempd: detected {} CPU(s)", cpus.len()); +struct CoretempScheme { + cpus: Vec, + next_handle: usize, + handles: Vec>, +} - let cpu_infos: Vec = cpus.iter().map(|&id| { - let vendor = detect_vendor(id); - let tjmax = if vendor == Vendor::Intel { - read_tjmax_intel(id) +impl CoretempScheme { + fn new(cpus: Vec) -> Self { + Self { + cpus, + next_handle: 1, + handles: vec![None], + } + } + + fn alloc_handle(&mut self, h: Handle) -> usize { + let id = self.next_handle; + self.handles.push(Some(h)); + self.next_handle += 1; + id + } + + fn get_handle(&self, id: usize) -> Result<&Handle> { + self.handles + .get(id) + .and_then(|opt| opt.as_ref()) + .ok_or_else(|| Error::new(EBADF)) + } + + fn format_listing(&self) -> String { + let mut out = String::new(); + for info in &self.cpus { + let temp = match info.vendor { + Vendor::Intel => read_temperature_intel(info.id, info.tjmax), + Vendor::Amd => read_temperature_amd(info.id), + Vendor::Unknown => None, + }; + match temp { + Some(t) => out.push_str(&format!("cpu{}: {}C\n", info.id, t)), + None => out.push_str(&format!("cpu{}: N/A\n", info.id)), + } + } + out + } +} + +impl SchemeSync for CoretempScheme { + fn scheme_root(&mut self) -> Result { + Ok(SCHEME_ROOT_ID) + } + + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if flags & O_ACCMODE != 0 { + return Err(Error::new(EINVAL)); + } + let handle = if dirfd == SCHEME_ROOT_ID { + let trimmed = path.trim_start_matches('/'); + if trimmed.is_empty() { + Handle::Listing + } else if let Some(cpu_str) = trimmed.strip_prefix("cpu") { + let cpu_id = cpu_str + .trim_end_matches('/') + .parse::() + .map_err(|_| Error::new(ENOENT))?; + if self.cpus.iter().any(|c| c.id == cpu_id) { + Handle::CpuTemp { cpu_id } + } else { + return Err(Error::new(ENOENT)); + } + } else { + return Err(Error::new(ENOENT)); + } } else { - 0 + return Err(Error::new(ENOENT)); }; - eprintln!("[INFO] coretempd: CPU {} = {:?}", id, vendor); - CpuInfo { id, vendor, tjmax } - }).collect(); - let infos_clone = cpu_infos.clone(); - thread::spawn(move || { - loop { - thread::sleep(Duration::from_millis(POLL_MS)); - for info in &infos_clone { + Ok(OpenResult::ThisScheme { + number: self.alloc_handle(handle), + flags: NewFdFlags::empty(), + }) + } + + fn read( + &mut self, + id: usize, + buf: &mut [u8], + offset: u64, + _flags: u32, + _ctx: &CallerCtx, + ) -> Result { + let data = match self.get_handle(id)? { + Handle::Listing => self.format_listing(), + Handle::CpuTemp { cpu_id } => { + let info = self + .cpus + .iter() + .find(|c| c.id == *cpu_id) + .ok_or_else(|| Error::new(ENOENT))?; let temp = match info.vendor { Vendor::Intel => read_temperature_intel(info.id, info.tjmax), Vendor::Amd => read_temperature_amd(info.id), Vendor::Unknown => None, }; - if let Some(t) = temp { - let _ = fs::write(format!("/tmp/coretemp_cpu{}", info.id), format!("{}\n", t)); + match temp { + Some(t) => format!("{}\n", t), + None => "N/A\n".to_string(), } } + }; + let bytes = data.as_bytes(); + let off = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?; + if off >= bytes.len() { + return Ok(0); + } + let count = (bytes.len() - off).min(buf.len()); + buf[..count].copy_from_slice(&bytes[off..off + count]); + Ok(count) + } + + fn write( + &mut self, + _id: usize, + _buf: &[u8], + _offset: u64, + _flags: u32, + _ctx: &CallerCtx, + ) -> Result { + Err(Error::new(EINVAL)) + } + + fn on_close(&mut self, id: usize) { + if id < self.handles.len() { + self.handles[id] = None; + } + } +} + +#[cfg(target_os = "redox")] +fn init_notify_fd() -> Option { + env::var("INIT_NOTIFY").ok()?.parse::().ok() +} + +#[cfg(target_os = "redox")] +fn notify_scheme_ready( + notify_fd: i32, + socket: &Socket, + scheme: &mut CoretempScheme, +) -> Result<(), String> { + let cap_id = scheme + .scheme_root() + .map_err(|err| format!("coretempd: scheme_root failed: {err}"))?; + let cap_fd = socket + .create_this_scheme_fd(0, cap_id, 0, 0) + .map_err(|err| format!("coretempd: create_this_scheme_fd failed: {err}"))?; + + syscall::call::write( + notify_fd as usize, + &libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(), + ) + .map_err(|err| format!("coretempd: failed to notify init: {err}"))?; + Ok(()) +} + +#[cfg(target_os = "redox")] +fn run_daemon() -> Result<(), String> { + let notify_fd = init_notify_fd(); + + let cpus = detect_cpus(); + eprintln!("[INFO] coretempd: detected {} CPU(s)", cpus.len()); + + let cpu_infos: Vec = cpus + .iter() + .map(|&id| { + let vendor = detect_vendor(id); + let tjmax = if vendor == Vendor::Intel { + read_tjmax_intel(id) + } else { + 0 + }; + eprintln!("[INFO] coretempd: CPU {} = {:?}", id, vendor); + CpuInfo { id, vendor, tjmax } + }) + .collect(); + + let socket = Socket::create() + .map_err(|err| format!("coretempd: failed to create scheme socket: {err}"))?; + let mut state = redox_scheme::scheme::SchemeState::new(); + let mut scheme = CoretempScheme::new(cpu_infos); + + if let Some(fd) = notify_fd { + notify_scheme_ready(fd, &socket, &mut scheme)?; + } + + eprintln!("[INFO] coretempd: registered scheme:coretemp"); + + let infos_clone = scheme.cpus.clone(); + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(POLL_MS)); + for info in &infos_clone { + let temp = match info.vendor { + Vendor::Intel => read_temperature_intel(info.id, info.tjmax), + Vendor::Amd => read_temperature_amd(info.id), + Vendor::Unknown => None, + }; + if let Some(t) = temp { + let _ = fs::write(format!("/tmp/coretemp_cpu{}", info.id), format!("{}\n", t)); + } } }); - for stream in listener.incoming() { - if let Ok(mut stream) = stream { - let mut buf = [0u8; 64]; - if let Ok(n) = stream.read(&mut buf) { - let req = String::from_utf8_lossy(&buf[..n]).trim().to_string(); - let resp = if req == "/" { - let mut names = String::new(); - for info in &cpu_infos { - names.push_str(&format!("cpu{}\n", info.id)); - } - names - } else if let Some(cpu_str) = req.strip_prefix("/cpu") { - if let Ok(cpu) = cpu_str.parse::() { - if let Some(info) = cpu_infos.iter().find(|i| i.id == cpu) { - let temp = match info.vendor { - Vendor::Intel => read_temperature_intel(info.id, info.tjmax), - Vendor::Amd => read_temperature_amd(info.id), - Vendor::Unknown => None, - }; - if let Some(t) = temp { - format!("{}\n", t) - } else { - "N/A\n".to_string() - } - } else { - "N/A\n".to_string() - } - } else { - "N/A\n".to_string() - } - } else { - "N/A\n".to_string() - }; - let _ = stream.write_all(resp.as_bytes()); - } + loop { + let request = socket + .next_request(SignalBehavior::Restart) + .map_err(|err| format!("coretempd: failed to read scheme request: {err}"))?; + + let Some(request) = request else { + return Ok(()); + }; + + if let redox_scheme::RequestKind::Call(request) = request.kind() { + let response = request.handle_sync(&mut scheme, &mut state); + socket + .write_response(response, SignalBehavior::Restart) + .map_err(|err| format!("coretempd: failed to write response: {err}"))?; } } } + +#[cfg(not(target_os = "redox"))] +fn run_daemon() -> Result<(), String> { + eprintln!("[INFO] coretempd: not running on Redox, exiting"); + Ok(()) +} + +fn main() { + if let Err(e) = run_daemon() { + eprintln!("[ERROR] coretempd: {e}"); + std::process::exit(1); + } +}