Files
RedBear-OS/logd/src/scheme.rs
T
bjorn3 400371eaf9 logd: Don't block receiving log messages on writing previous ones
This avoid a deadlock when the process logging things is directly or
indirectly involved in receiving them too. Currently there is a
workaround for a part of this problem in fbbootlogd, but doing it
directly in logd is a lot simpler and catches more cases.
2025-03-15 16:23:51 +01:00

165 lines
3.9 KiB
Rust

use std::collections::BTreeMap;
use std::fs::File;
use std::io::Write;
use std::mem;
use std::sync::mpsc::{self, Sender};
use redox_scheme::SchemeMut;
use syscall::error::*;
pub struct LogHandle {
context: Box<str>,
bufs: BTreeMap<usize, Vec<u8>>,
}
pub struct LogScheme {
next_id: usize,
output_tx: Sender<Vec<u8>>,
handles: BTreeMap<usize, LogHandle>,
pub current_pid: usize,
}
impl LogScheme {
pub fn new(mut files: Vec<File>) -> Self {
let (output_tx, output_rx) = mpsc::channel::<Vec<u8>>();
std::thread::spawn(move || {
for line in output_rx {
for file in &mut files {
let _ = file.write(&line);
let _ = file.flush();
}
}
});
LogScheme {
next_id: 0,
output_tx,
handles: BTreeMap::new(),
current_pid: 0,
}
}
}
impl SchemeMut for LogScheme {
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
let id = self.next_id;
self.next_id += 1;
self.handles.insert(
id,
LogHandle {
context: path.to_string().into_boxed_str(),
bufs: BTreeMap::new(),
},
);
Ok(id)
}
fn dup(&mut self, old_id: usize, buf: &[u8]) -> Result<usize> {
if !buf.is_empty() {
return Err(Error::new(EINVAL));
}
let context = {
let handle = self.handles.get(&old_id).ok_or(Error::new(EBADF))?;
handle.context.clone()
};
let id = self.next_id;
self.next_id += 1;
self.handles.insert(
id,
LogHandle {
context,
bufs: BTreeMap::new(),
},
);
Ok(id)
}
fn read(&mut self, id: usize, _buf: &mut [u8], _offset: u64, _flags: u32) -> Result<usize> {
let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
// TODO
Ok(0)
}
fn write(&mut self, id: usize, buf: &[u8], _offset: u64, _flags: u32) -> Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let handle_buf = handle
.bufs
.entry(self.current_pid)
.or_insert_with(|| Vec::new());
let mut i = 0;
while i < buf.len() {
let b = buf[i];
if handle_buf.is_empty() && !handle.context.is_empty() {
handle_buf.extend_from_slice(handle.context.as_bytes());
handle_buf.extend_from_slice(b": ");
}
handle_buf.push(b);
if b == b'\n' {
self.output_tx.send(mem::take(handle_buf)).unwrap();
}
i += 1;
}
Ok(i)
}
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
Ok(0)
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
let scheme_path = b"log:";
let mut i = 0;
while i < buf.len() && i < scheme_path.len() {
buf[i] = scheme_path[i];
i += 1;
}
let mut j = 0;
let context_bytes = handle.context.as_bytes();
while i < buf.len() && j < context_bytes.len() {
buf[i] = context_bytes[j];
i += 1;
j += 1;
}
Ok(i)
}
fn fsync(&mut self, id: usize) -> Result<usize> {
let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
//TODO: flush remaining data?
Ok(0)
}
fn close(&mut self, id: usize) -> Result<usize> {
self.handles.remove(&id).ok_or(Error::new(EBADF))?;
//TODO: flush remaining data?
Ok(0)
}
}