diff --git a/logd/.gitignore b/logd/.gitignore new file mode 100644 index 0000000000..eb5a316cbd --- /dev/null +++ b/logd/.gitignore @@ -0,0 +1 @@ +target diff --git a/logd/Cargo.lock b/logd/Cargo.lock new file mode 100644 index 0000000000..2f9ab2c892 --- /dev/null +++ b/logd/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "logd" +version = "0.1.0" +dependencies = [ + "libredox", + "redox-daemon", + "redox-scheme", + "redox_syscall", +] + +[[package]] +name = "redox-daemon" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdeffdb03cf2961b211a2023e683d71f2c225ea93404da5dc34b0dc94f0341" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "redox-scheme" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d040cfa6370d9c6b1a5987247272f19ea4722f1064e66ad2e077c152b82ea3" +dependencies = [ + "libredox", + "redox_syscall", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] diff --git a/logd/Cargo.toml b/logd/Cargo.toml new file mode 100644 index 0000000000..ab9f15e31a --- /dev/null +++ b/logd/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "logd" +version = "0.1.0" +authors = ["Jeremy Soller "] +edition = "2021" + +[dependencies] +redox_syscall = "0.5" +redox-daemon = "0.1.2" +libredox = "0.1.3" +redox-scheme = "0.2.1" diff --git a/logd/README.md b/logd/README.md new file mode 100644 index 0000000000..be1f2d5f1b --- /dev/null +++ b/logd/README.md @@ -0,0 +1,19 @@ +# logd + +Log daemon for collecting all log output. + +## How To Contribute + +To learn how to contribute to this system component you need to read the following document: + +- [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md) + +## Development + +To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages. + +### How To Build + +To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. + +This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux. diff --git a/logd/src/main.rs b/logd/src/main.rs new file mode 100644 index 0000000000..11b04c8d41 --- /dev/null +++ b/logd/src/main.rs @@ -0,0 +1,50 @@ +use redox_scheme::{RequestKind, SignalBehavior, Socket, V2}; +use std::env; +use std::fs::OpenOptions; +use std::process; + +use crate::scheme::LogScheme; + +mod scheme; + +fn daemon(daemon: redox_daemon::Daemon) -> ! { + let mut files = Vec::new(); + for arg in env::args().skip(1) { + eprintln!("logd: opening {:?}", arg); + match OpenOptions::new().write(true).open(&arg) { + Ok(file) => files.push(file), + Err(err) => eprintln!("logd: failed to open {:?}: {:?}", arg, err), + } + } + + let socket = Socket::::create("log").expect("logd: failed to create log scheme"); + + libredox::call::setrens(0, 0).expect("logd: failed to enter null namespace"); + + eprintln!("logd: ready for logging on log:"); + + daemon.ready().expect("logd: failed to notify parent"); + + let mut scheme = LogScheme::new(files); + + while let Some(request) = socket + .next_request(SignalBehavior::Restart) + .expect("logd: failed to read events from log scheme") + { + scheme.current_pid = request.context_id(); + + let RequestKind::Call(request) = request.kind() else { + continue; + }; + + let response = request.handle_scheme_mut(&mut scheme); + socket + .write_responses(&[response], SignalBehavior::Restart) + .expect("logd: failed to write responses to log scheme"); + } + process::exit(0); +} + +fn main() { + redox_daemon::Daemon::new(daemon).expect("logd: failed to daemonize"); +} diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs new file mode 100644 index 0000000000..f50ce41769 --- /dev/null +++ b/logd/src/scheme.rs @@ -0,0 +1,156 @@ +use std::collections::BTreeMap; +use std::fs::File; +use std::io::Write; + +use redox_scheme::SchemeMut; +use syscall::error::*; + +pub struct LogHandle { + context: Box, + bufs: BTreeMap>, +} + +pub struct LogScheme { + next_id: usize, + files: Vec, + handles: BTreeMap, + pub current_pid: usize, +} + +impl LogScheme { + pub fn new(files: Vec) -> Self { + LogScheme { + next_id: 0, + files, + handles: BTreeMap::new(), + current_pid: 0, + } + } +} + +impl SchemeMut for LogScheme { + fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { + 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 { + 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 { + 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 { + 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' { + for file in self.files.iter_mut() { + let _ = file.write(&handle_buf); + let _ = file.flush(); + } + + handle_buf.clear(); + } + + i += 1; + } + + Ok(i) + } + + fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + Ok(0) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + 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 { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + //TODO: flush remaining data? + + Ok(0) + } + + fn close(&mut self, id: usize) -> Result { + self.handles.remove(&id).ok_or(Error::new(EBADF))?; + + //TODO: flush remaining data? + + Ok(0) + } +}