milestone: desktop path Phases 1-5
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "logd"
|
||||
description = "Logging daemon"
|
||||
version = "0.1.0"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
redox_syscall.workspace = true
|
||||
daemon = { path = "../daemon" }
|
||||
libredox.workspace = true
|
||||
redox-scheme.workspace = true
|
||||
scheme-utils = { path = "../scheme-utils" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,25 @@
|
||||
use redox_scheme::Socket;
|
||||
use scheme_utils::Blocking;
|
||||
|
||||
use crate::scheme::LogScheme;
|
||||
|
||||
mod scheme;
|
||||
|
||||
fn daemon(daemon: daemon::SchemeDaemon) -> ! {
|
||||
let socket = Socket::create().expect("logd: failed to create log scheme");
|
||||
|
||||
let mut scheme = LogScheme::new(&socket);
|
||||
let handler = Blocking::new(&socket, 16);
|
||||
|
||||
let _ = daemon.ready_sync_scheme(&socket, &mut scheme);
|
||||
|
||||
libredox::call::setrens(0, 0).expect("logd: failed to enter null namespace");
|
||||
|
||||
handler
|
||||
.process_requests_blocking(scheme)
|
||||
.expect("logd: failed to process requests");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
daemon::SchemeDaemon::new(daemon);
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
use std::mem;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult, SendFdRequest, Socket};
|
||||
use scheme_utils::{FpathWriter, HandleMap};
|
||||
use syscall::error::*;
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
|
||||
pub enum LogHandle {
|
||||
Log {
|
||||
context: Box<str>,
|
||||
bufs: BTreeMap<usize, Vec<u8>>,
|
||||
},
|
||||
AddSink,
|
||||
SchemeRoot,
|
||||
}
|
||||
|
||||
pub struct LogScheme<'sock> {
|
||||
socket: &'sock Socket,
|
||||
kernel_debug: File,
|
||||
output_tx: Sender<OutputCmd>,
|
||||
handles: HandleMap<LogHandle>,
|
||||
}
|
||||
|
||||
enum OutputCmd {
|
||||
Log(Vec<u8>),
|
||||
AddSink(usize),
|
||||
}
|
||||
|
||||
impl<'sock> LogScheme<'sock> {
|
||||
pub fn new(socket: &'sock Socket) -> Self {
|
||||
let kernel_debug = OpenOptions::new()
|
||||
.write(true)
|
||||
.open("/scheme/debug")
|
||||
.unwrap();
|
||||
|
||||
let mut kernel_sys_log = std::fs::File::open("/scheme/sys/log").unwrap();
|
||||
|
||||
let (output_tx, output_rx) = mpsc::channel::<OutputCmd>();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut files: Vec<File> = vec![];
|
||||
let mut logs = VecDeque::new();
|
||||
for cmd in output_rx {
|
||||
match cmd {
|
||||
OutputCmd::Log(line) => {
|
||||
for file in &mut files {
|
||||
let _ = file.write(&line);
|
||||
let _ = file.flush();
|
||||
}
|
||||
logs.push_back(line);
|
||||
// Keep a limited amount of logs for backfilling to bound memory usage
|
||||
while logs.len() > 1000 {
|
||||
logs.pop_front();
|
||||
}
|
||||
}
|
||||
OutputCmd::AddSink(log_fd) => {
|
||||
let mut file = unsafe { File::from_raw_fd(log_fd as RawFd) };
|
||||
for line in &logs {
|
||||
let _ = file.write(line);
|
||||
let _ = file.flush();
|
||||
}
|
||||
|
||||
files.push(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let output_tx2 = output_tx.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut handle_buf = vec![];
|
||||
let mut buf = [0; 4096];
|
||||
buf[.."kernel: ".len()].copy_from_slice(b"kernel: ");
|
||||
loop {
|
||||
let n = kernel_sys_log.read(&mut buf["kernel: ".len()..]).unwrap();
|
||||
if n == 0 {
|
||||
// FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
|
||||
break;
|
||||
}
|
||||
Self::write_logs(&output_tx2, &mut handle_buf, "kernel", &buf, None);
|
||||
}
|
||||
});
|
||||
|
||||
LogScheme {
|
||||
socket,
|
||||
kernel_debug,
|
||||
output_tx,
|
||||
handles: HandleMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_logs(
|
||||
output_tx: &Sender<OutputCmd>,
|
||||
handle_buf: &mut Vec<u8>,
|
||||
context: &str,
|
||||
buf: &[u8],
|
||||
mut kernel_debug: Option<&mut File>,
|
||||
) {
|
||||
let mut i = 0;
|
||||
while i < buf.len() {
|
||||
let b = buf[i];
|
||||
|
||||
if handle_buf.is_empty() && !context.is_empty() {
|
||||
handle_buf.extend_from_slice(context.as_bytes());
|
||||
handle_buf.extend_from_slice(b": ");
|
||||
}
|
||||
|
||||
handle_buf.push(b);
|
||||
|
||||
if b == b'\n' {
|
||||
if let Some(kernel_debug) = kernel_debug.as_mut() {
|
||||
// Writing to the kernel debug log never blocks
|
||||
let _ = kernel_debug.write(handle_buf);
|
||||
let _ = kernel_debug.flush();
|
||||
}
|
||||
|
||||
output_tx
|
||||
.send(OutputCmd::Log(mem::take(handle_buf)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sock> SchemeSync for LogScheme<'sock> {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
Ok(self.handles.insert(LogHandle::SchemeRoot))
|
||||
}
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
_flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
if !matches!(self.handles.get(dirfd)?, LogHandle::SchemeRoot) {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let id = if path == "add_sink" {
|
||||
self.handles.insert(LogHandle::AddSink)
|
||||
} else {
|
||||
self.handles.insert(LogHandle::Log {
|
||||
context: path.to_string().into_boxed_str(),
|
||||
bufs: BTreeMap::new(),
|
||||
})
|
||||
};
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
_buf: &mut [u8],
|
||||
_offset: u64,
|
||||
_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let _handle = self.handles.get(id)?;
|
||||
|
||||
// TODO
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
_offset: u64,
|
||||
_flags: u32,
|
||||
ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let (context, bufs) = match self.handles.get_mut(id)? {
|
||||
LogHandle::Log { context, bufs } => (context, bufs),
|
||||
LogHandle::SchemeRoot | LogHandle::AddSink => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
let handle_buf = bufs.entry(ctx.pid).or_insert_with(|| Vec::new());
|
||||
|
||||
Self::write_logs(
|
||||
&self.output_tx,
|
||||
handle_buf,
|
||||
context,
|
||||
buf,
|
||||
Some(&mut self.kernel_debug),
|
||||
);
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result<usize> {
|
||||
let id = sendfd_request.id();
|
||||
|
||||
if !matches!(self.handles.get(id)?, LogHandle::AddSink) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let mut new_fd = usize::MAX;
|
||||
if let Err(e) = sendfd_request.obtain_fd(
|
||||
&self.socket,
|
||||
syscall::FobtainFdFlags::CLOEXEC,
|
||||
std::slice::from_mut(&mut new_fd),
|
||||
) {
|
||||
return Err(e);
|
||||
}
|
||||
self.output_tx.send(OutputCmd::AddSink(new_fd)).unwrap();
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
let _handle = self.handles.get(id)?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
FpathWriter::with(buf, "log", |w| {
|
||||
w.push_str(match self.handles.get(id)? {
|
||||
LogHandle::Log { context, .. } => context,
|
||||
LogHandle::AddSink => "add_sink",
|
||||
LogHandle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
let _handle = self.handles.get(id)?;
|
||||
|
||||
//TODO: flush remaining data?
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
self.handles.remove(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user