diff --git a/Cargo.lock b/Cargo.lock index 5627349..99a7b16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,7 @@ dependencies = [ name = "lolicron" version = "0.1.0" dependencies = [ + "bitflags", "cfg-if", "chrono", "config_struct", diff --git a/Cargo.toml b/Cargo.toml index afdcb20..9556ea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ libc = "0.2" cfg-if = "0.1" generational-arena = "0.2" mopa = "0.2" +readable-perms = "0.1" [build-dependencies] rustc_version = "0.2" diff --git a/src/log/builder.rs b/src/log/builder.rs new file mode 100644 index 0000000..7d3bd4d --- /dev/null +++ b/src/log/builder.rs @@ -0,0 +1,14 @@ +//! Builder pattern for logging options + +use super::*; +use std::{ + path::{ + PathBuf, + }, +}; + +pub struct LoggingOptions +{ + output_logfile: Some((PathBuf, u32)), +} + diff --git a/src/log/custom.rs b/src/log/custom.rs index c833836..0de7b4b 100644 --- a/src/log/custom.rs +++ b/src/log/custom.rs @@ -26,3 +26,9 @@ impl std::fmt::Debug for dyn Hook write!(f, "") } } + + +// Customs + +mod global_logfile; +pub use global_logfile::*; diff --git a/src/log/custom/global_logfile.rs b/src/log/custom/global_logfile.rs new file mode 100644 index 0000000..d20ec5b --- /dev/null +++ b/src/log/custom/global_logfile.rs @@ -0,0 +1,141 @@ +//! Logging hooks for logfile output + +use super::*; +use std::{ + path::{ + PathBuf, + Path, + }, + sync::Arc, + io::{ + self, + ErrorKind, + }, +}; +use tokio::{ + prelude::*, + sync::{ + mpsc, + }, + task, + fs::{OpenOptions,File,}, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Internal +{ + stdout: PathBuf, + stderr: PathBuf, + + use_local_time: bool, +} + +#[derive(Debug)] +struct Command +{ + level: Level, + + trace: Trace, + timestamp: Date, + message: String, + + is_err: bool, +} + +#[derive(Debug)] +/// Logging hook that appends to logfiles +pub struct LogFileHook +{ + mask: Level, + + internal: Arc, + + sender: Option>, +} + +impl LogFileHook +{ + /// Create a new log file hook + pub fn new(mask: L, path: P, error: E, use_local_time: bool) -> Self + where L: Into, + P: AsRef, + E: AsRef + { + Self { + mask: mask.into(), + internal: Arc::new(Internal{ + stderr: error.as_ref().to_owned(), + stdout: path.as_ref().to_owned(), + use_local_time, + }), + sender: None, + } + } +} + +async fn write_log(file: &mut File, com: Command) -> std::io::Result<()> +{ + let output = format!("{} <{}> [{}]: {}\n", com.timestamp, com.trace, com.level, com.message); + file.write(output.as_bytes()).await?; + Ok(()) +} + +const BACKLOG: usize = 100; + +impl Hook for LogFileHook +{ + fn initialise(&mut self, _: &Index) + { + let (tx, mut rx) = mpsc::channel(BACKLOG); + + let internal = Arc::clone(&self.internal); + + self.sender = Some(tx); + task::spawn(async move { + let (stdout, stderr) = (Path::new(&internal.stdout), Path::new(&internal.stderr)); + + while let Some(command) = rx.recv().await { + let path = if command.is_err {stderr} else {stdout}; + + let mut file = match OpenOptions::new() + .append(true) + .create(true) + .open(path).await { + Ok(file) => file, + Err(err) => { + crate::warn!("Could not open logfile {:?} for writing: {}", path, err); + continue; + }, + }; + + if let Err(err) = write_log(&mut file, command).await { + crate::warn!("Failed writing to logfile {:?}: {}", path, err); + } + } + }); + } + + fn finalise(&mut self, _: Index) + { + + } + + fn println(&mut self, level: &dyn AsLevel, what: &dyn Display, trace: &Trace) -> io::Result<()> + { + let com = Command { + timestamp: if self.internal.use_local_time {Date::now_local()} else {Date::now_utc()}, + is_err: level.is_err(), + trace: trace.clone(), + message: what.to_string(), + level: level.borrow().clone(), + }; + + if let Some(sender) = &mut self.sender { + if let Err(e) = sender.try_send(com) { + return Err(io::Error::new(ErrorKind::BrokenPipe, format!("Failed to pass to worker: {}", e))); + } + } + + Ok(()) + } +} diff --git a/src/log/level.rs b/src/log/level.rs index afb56f4..e9d65ae 100644 --- a/src/log/level.rs +++ b/src/log/level.rs @@ -19,10 +19,17 @@ pub trait AsLevel: fmt::Display + Borrow{ { write!(f, "{}/", Borrow::::borrow(self)) } + #[inline] fn is_err(&self) -> bool {self.borrow().is_err()} } impl AsLevel for Level{ #[inline] fn prefix(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result{Ok(())} + #[inline] fn is_err(&self) -> bool { + match self { + Self::Error | Self::Warn | Self::Silent => true, + _ => false, + } + } } impl AsLevel for &Level{ #[inline] fn prefix(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result{Ok(())} diff --git a/src/log/mod.rs b/src/log/mod.rs index b588966..b5cf642 100644 --- a/src/log/mod.rs +++ b/src/log/mod.rs @@ -41,6 +41,58 @@ pub struct Logger static INSTANCE: OnceCell = OnceCell::new(); +#[derive(Debug)] +/// Dynamic date wrapper +enum Date { + /// Local time + Local(chrono::DateTime), + /// Utc time + Utc(chrono::DateTime), +} +impl Date +{ + /// Timestamp for now (local time) + pub fn now_local() -> Self + { + chrono::offset::Local::now().into() + } + /// Timestamp for now (UTC time) + pub fn now_utc() -> Self + { + chrono::offset::Utc::now().into() + } +} + +impl std::fmt::Display for Date +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::Local(l) => write!(f, "{}", l.format(DATE_FORMAT.as_ref())), + Self::Utc(l) => write!(f, "{}", l.format(DATE_FORMAT.as_ref())), + } + } +} +impl From> for Date +{ + #[inline] + fn from(from: chrono::DateTime) -> Self + { + Self::Local(from) + } +} + +impl From> for Date +{ + #[inline] + fn from(from: chrono::DateTime) -> Self + { + Self::Utc(from) + } +} + + impl Logger { /// Create a new logger @@ -171,39 +223,7 @@ impl Logger { self.dispatch(&level, &what, &trace); //lol - enum Date { - Local(chrono::DateTime), - Utc(chrono::DateTime), - } - impl std::fmt::Display for Date - { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - match self { - Self::Local(l) => write!(f, "{}", l.format(DATE_FORMAT.as_ref())), - Self::Utc(l) => write!(f, "{}", l.format(DATE_FORMAT.as_ref())), - } - } - } - impl From> for Date - { - #[inline] - fn from(from: chrono::DateTime) -> Self - { - Self::Local(from) - } - } - - impl From> for Date - { - #[inline] - fn from(from: chrono::DateTime) -> Self - { - Self::Utc(from) - } - } - + if &self.level >= level.borrow() { let now: Date = if self.use_local_time { @@ -365,7 +385,6 @@ impl Logger }; } - #[macro_export] macro_rules! dangerous { ($obj:expr) => { { diff --git a/src/main.rs b/src/main.rs index 537fd4d..039d171 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,8 @@ mod hot; mod context; mod job; +mod perms; + //This is a test function, when we have a real job server, remove it. async fn do_thing_every() -> Result<(mpsc::Sender<()>, task::JoinHandle<()>), Box> { @@ -123,6 +125,7 @@ fn print_stats() async fn main() -> Result<(), Box> { log::init(log::Level::Debug); + //log::Logger::global().add_hook(log::custom::LogFileHook::new(log::Level::Debug, "test.log", "test.err.log", false)); debug!("Logger initialised"); //TODO: Parse config first print_stats(); diff --git a/src/perms.rs b/src/perms.rs new file mode 100644 index 0000000..1b10346 --- /dev/null +++ b/src/perms.rs @@ -0,0 +1,202 @@ +//! Permissions shit kms +//! +//! No I don't care about Microshaft wangblows +#![allow(non_upper_case_globals)] +use bitflags::bitflags; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum Class +{ + Owner(Bit), + Group(Bit), + Other(Bit), +} + +impl Class +{ + /// Lmfao. Fuck bit masks and unix permissiong, `const fn` so idc + const fn mode(&self) -> u32 + { + macro_rules! map { + ($bit:expr, $bt:path, $constant:ident) => { + if $bit.contains($bt) { + $constant + } else { + 0 + } + } + } + match self { + Self::Owner(bit) => { + 0u32 | + map!(bit, Bit::Read, MODE_USER_READ) | + map!(bit, Bit::Write, MODE_USER_WRITE) | + map!(bit, Bit::Execute, MODE_USER_EXECUTE) + }, + Self::Group(bit) => { + 0u32 | + map!(bit, Bit::Read, MODE_GROUP_READ) | + map!(bit, Bit::Write, MODE_GROUP_WRITE) | + map!(bit, Bit::Execute, MODE_GROUP_EXECUTE) + }, + Self::Other(bit) => { + 0u32 | + map!(bit, Bit::Read, MODE_OTHER_READ) | + map!(bit, Bit::Write, MODE_OTHER_WRITE) | + map!(bit, Bit::Execute, MODE_OTHER_EXECUTE) + }, + } + } + + const fn mask_mode(&self, bit: u32) -> Bit + { + macro_rules! map { + ($bit:expr, $bt:path, $constant:ident) => { + if ($bit & $constant) == $constant {$bt.bits()} else {0u32} + } + } + + Bit::from_bits_truncate(match self { + Self::Owner(_) => { + map!(bit, Bit::Read, MODE_USER_READ) | + map!(bit, Bit::Write, MODE_USER_WRITE) | + map!(bit, Bit::Execute, MODE_USER_EXECUTE) + }, + Self::Group(_) => { + map!(bit, Bit::Read, MODE_GROUP_READ) | + map!(bit, Bit::Write, MODE_GROUP_WRITE) | + map!(bit, Bit::Execute, MODE_GROUP_EXECUTE) + }, + Self::Other(_) => { + map!(bit, Bit::Read, MODE_OTHER_READ) | + map!(bit, Bit::Write, MODE_OTHER_WRITE) | + map!(bit, Bit::Execute, MODE_OTHER_EXECUTE) + }, + }) + } +} + +const MODE_USER: u32 = 0o700; +const MODE_USER_READ: u32 = 0o400; +const MODE_USER_WRITE: u32 = 0o200; +const MODE_USER_EXECUTE: u32 = 0o100; + +const MODE_GROUP: u32 = 0o70; +const MODE_GROUP_READ: u32 = 0o40; +const MODE_GROUP_WRITE: u32 = 0o20; +const MODE_GROUP_EXECUTE: u32 = 0o10; + +const MODE_OTHER: u32 = 0o7; +const MODE_OTHER_READ: u32 = 0o4; +const MODE_OTHER_WRITE: u32 = 0o2; +const MODE_OTHER_EXECUTE: u32 = 0o1; + +const MODE: u32 = 0o777; + +bitflags!{ + pub struct Bit: u32 { + const None = 0; + const Read = 1; + const Write = 2; + const Execute = 4; + const Mask = 7; + } +} + +bitflags!{ + struct StickyBit: u32{ + const None = 0; + const Setuid = 0o40000; + const Setgid = 0o20000; + const SaveSwap = 0o10000; + + } +} + +/// Permissions struct in readable format +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)] +pub struct Permissions +{ + pub u_owner: Bit, + pub u_group: Bit, + pub u_other: Bit, + + _u_sticky: StickyBit, //idc about this either +} + +impl Default for Permissions +{ + #[inline] fn default() -> Self + { + Self::new() + } +} + +impl Permissions +{ + /// Create a new empty `Permissions` struct + pub const fn new() -> Self + { + Self { + u_owner: Bit::None, + u_group: Bit::None, + u_other: Bit::None, + _u_sticky: StickyBit::None, + } + } +} + +// Mask impl +impl Permissions +{ + #[inline] const fn c_owner(&self) -> Class + { + Class::Owner(self.u_owner) + } + #[inline] const fn c_group(&self) -> Class + { + Class::Group(self.u_group) + } + #[inline] const fn c_other(&self) -> Class + { + Class::Other(self.u_other) + } + + /// Convert into `mode_t` repr. + #[inline] pub const fn mask(&self) -> u32 + { + self.c_owner().mode() | + self.c_group().mode() | + self.c_other().mode() + } + + /// Convert from `mode_t` repr. + #[inline] pub const fn from_mask(bit: u32) -> Self + { + Self{ + u_owner: Class::Owner(Bit::Mask).mask_mode(bit), + u_group: Class::Group(Bit::Mask).mask_mode(bit), + u_other: Class::Other(Bit::Mask).mask_mode(bit), + + _u_sticky: StickyBit::None, + } + } +} + +impl From for u32 +{ + #[inline] fn from(from: Permissions) -> Self + { + from.mask() + } +} + +impl From for Permissions +{ + #[inline] fn from(from: u32) -> Self + { + Self::from_mask(from) + } +} + + diff --git a/test.log b/test.log new file mode 100644 index 0000000..6d4695f --- /dev/null +++ b/test.log @@ -0,0 +1,21 @@ +2020-08-05 01:04:28 UTC [Debug]: Logger initialised2020-08-05 01:04:28 UTC [Debug]: This is the lolicron daemon version 0.1.0 by Avril , Mx. Package Install (debug build)2020-08-05 01:04:28 UTC [Debug]: ---2020-08-05 01:04:28 UTC [Debug]: Compiled with (on (default), off (default)):2020-08-05 01:04:28 UTC [Debug]:  +nightly2020-08-05 01:04:28 UTC [Debug]:  +debug_assertions2020-08-05 01:04:28 UTC [Debug]: features:2020-08-05 01:04:28 UTC [Debug]:  +threaded2020-08-05 01:04:28 UTC [Debug]:  +watcher2020-08-05 01:04:28 UTC [Debug]:  +debug_logger2020-08-05 01:04:28 UTC [Debug]:  -watcher_unlimited2020-08-05 01:04:28 UTC [Debug]:  +watcher_timeout2020-08-05 01:04:28 UTC [Debug]: 2020-08-05 01:04:28 UTC [Debug]: Static options:2020-08-05 01:04:28 UTC [Debug]: hot::BACKLOG = 1002020-08-05 01:04:28 UTC [Debug]: hot::TIMEOUT = 52020-08-05 01:04:28 UTC [Debug]: GPl'd with <32020-08-05 01:04:28 UTC [Debug]: Please enjoy2020-08-05 01:04:28 UTC [Debug]: ---2020-08-05 01:04:28 UTC [Debug]: Watcher initialised, starting for path "/home/avril/work/lolicron"2020-08-05 01:04:28 UTC [Info]: Watcher up2020-08-05 01:05:39 UTC [Debug]: Logger initialised +2020-08-05 01:05:39 UTC [Debug]: This is the lolicron daemon version 0.1.0 by Avril , Mx. Package Install (debug build) +2020-08-05 01:05:39 UTC [Debug]: --- +2020-08-05 01:05:39 UTC [Debug]: Compiled with (on (default), off (default)): +2020-08-05 01:05:39 UTC [Debug]:  +nightly +2020-08-05 01:05:39 UTC [Debug]:  +debug_assertions +2020-08-05 01:05:39 UTC [Debug]: features: +2020-08-05 01:05:39 UTC [Debug]:  +threaded +2020-08-05 01:05:39 UTC [Debug]:  +watcher +2020-08-05 01:05:39 UTC [Debug]:  +debug_logger +2020-08-05 01:05:39 UTC [Debug]:  -watcher_unlimited +2020-08-05 01:05:39 UTC [Debug]:  +watcher_timeout +2020-08-05 01:05:39 UTC [Debug]: +2020-08-05 01:05:39 UTC [Debug]: Static options: +2020-08-05 01:05:39 UTC [Debug]: hot::BACKLOG = 100 +2020-08-05 01:05:39 UTC [Debug]: hot::TIMEOUT = 5 +2020-08-05 01:05:39 UTC [Debug]: GPl'd with <3 +2020-08-05 01:05:39 UTC [Debug]: Please enjoy +2020-08-05 01:05:39 UTC [Debug]: --- +2020-08-05 01:05:39 UTC [Debug]: Watcher initialised, starting for path "/home/avril/work/lolicron" +2020-08-05 01:05:39 UTC [Info]: Watcher up