diff --git a/bffhd/capnp/user_system.rs b/bffhd/capnp/user_system.rs index 2c735d9..2d40a44 100644 --- a/bffhd/capnp/user_system.rs +++ b/bffhd/capnp/user_system.rs @@ -56,7 +56,8 @@ impl manage::Server for Users { .get_all() .map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e)))); let mut builder = result.get().init_user_list(users.len() as u32); - for (i, (_, user)) in users.into_iter().enumerate() { + for (i, (id, userdata)) in users.into_iter().enumerate() { + let user = db::User { id, userdata }; User::fill(&self.session, user, builder.reborrow().get(i as u32)); } diff --git a/bffhd/lib.rs b/bffhd/lib.rs index 422813a..81dd9ea 100644 --- a/bffhd/lib.rs +++ b/bffhd/lib.rs @@ -65,7 +65,6 @@ use lightproc::recoverable_handle::RecoverableHandle; use signal_hook::consts::signal::*; use tracing::Span; - pub struct Diflouroborane { config: Config, executor: Executor<'static>, @@ -84,8 +83,7 @@ impl error::Description for SignalHandlerErr { } impl Diflouroborane { - pub fn setup() { - } + pub fn setup() {} pub fn new(config: Config) -> miette::Result { let mut server = logging::init(&config.logging); @@ -208,4 +206,4 @@ impl ShutdownHandler { handle.cancel() } } -} \ No newline at end of file +} diff --git a/bffhd/logging.rs b/bffhd/logging.rs index 8e7f100..1fdb2c6 100644 --- a/bffhd/logging.rs +++ b/bffhd/logging.rs @@ -1,9 +1,9 @@ -use std::path::Path; -use tracing_subscriber::{EnvFilter, reload}; use serde::{Deserialize, Serialize}; +use std::path::Path; use tracing_subscriber::fmt::format::Format; use tracing_subscriber::prelude::*; use tracing_subscriber::reload::Handle; +use tracing_subscriber::{reload, EnvFilter}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LogConfig { @@ -35,7 +35,7 @@ pub enum LogOutput<'a> { pub struct LogConfig2<'a, F> { output: LogOutput<'a>, filter_str: Option<&'a str>, - format: Format + format: Format, } pub fn init(config: &LogConfig) -> console::Server { @@ -56,20 +56,15 @@ pub fn init(config: &LogConfig) -> console::Server { match format.as_ref() { "pretty" => { - let fmt_layer = fmt_layer - .pretty() - .with_filter(filter); + let fmt_layer = fmt_layer.pretty().with_filter(filter); subscriber.with(fmt_layer).init(); } "compact" => { - let fmt_layer = fmt_layer - .compact() - .with_filter(filter); + let fmt_layer = fmt_layer.compact().with_filter(filter); subscriber.with(fmt_layer).init(); } _ => { - let fmt_layer = fmt_layer - .with_filter(filter); + let fmt_layer = fmt_layer.with_filter(filter); subscriber.with(fmt_layer).init(); } } diff --git a/bffhd/users/db.rs b/bffhd/users/db.rs index a8a804a..cc13b20 100644 --- a/bffhd/users/db.rs +++ b/bffhd/users/db.rs @@ -187,15 +187,15 @@ impl UserDB { Ok(()) } - pub fn get_all(&self) -> Result, db::Error> { + pub fn get_all(&self) -> Result, db::Error> { let txn = self.env.begin_ro_txn()?; let iter = self.db.get_all(&txn)?; - let mut out = Vec::new(); + let mut out = HashMap::new(); for (uid, user) in iter { let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() }; let user: User = Deserialize::::deserialize(user.as_ref(), &mut Infallible).unwrap(); - out.push((uid, user)); + out.insert(uid, user.userdata); } Ok(out) diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index 8471dbf..01360e2 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -1,8 +1,11 @@ +use std::fs; + use lmdb::{Environment, Transaction}; use once_cell::sync::OnceCell; use rkyv::{Archive, Deserialize, Infallible, Serialize}; use std::collections::HashMap; -use std::fmt::{Display, Formatter, Write}; +use std::fmt::{Display, Formatter}; +use std::io::Write; use clap::ArgMatches; use miette::{Context, Diagnostic, IntoDiagnostic, SourceOffset, SourceSpan}; @@ -166,4 +169,41 @@ impl Users { txn.commit().map_err(crate::db::Error::from)?; Ok(()) } + + pub fn dump_file(&self, path_str: &str, force: bool) -> miette::Result { + let path = Path::new(path_str); + let exists = path.exists(); + if exists { + if !force { + #[derive(Debug, Error, Diagnostic)] + #[error("given file already exists, refusing to clobber")] + #[diagnostic(code(dump::clobber))] + struct DumpFileExists { + #[source_code] + src: String, + + #[label("file provided")] + dir_path: SourceSpan, + + #[help] + help: &'static str, + } + + Err(DumpFileExists { + src: format!("--load {}", path_str), + dir_path: (7, path_str.as_bytes().len()).into(), + help: "to force overwriting the file add `--force` as argument", + })?; + } else { + tracing::info!("output file already exists, overwriting due to `--force`"); + } + } + let mut file = fs::File::create(path).into_diagnostic()?; + + let users = self.userdb.get_all()?; + let encoded = toml::ser::to_vec(&users).into_diagnostic()?; + file.write_all(&encoded[..]).into_diagnostic()?; + + Ok(0) + } } diff --git a/bin/bffhd/main.rs b/bin/bffhd/main.rs index 23b2a0f..d52cbca 100644 --- a/bin/bffhd/main.rs +++ b/bin/bffhd/main.rs @@ -1,4 +1,4 @@ -use clap::{Arg, Command}; +use clap::{Arg, Command, ValueHint}; use diflouroborane::{config, Diflouroborane}; use std::str::FromStr; @@ -61,6 +61,20 @@ fn main() -> miette::Result<()> { .help("Dump all internal databases") .long("dump") .conflicts_with("load")) + .arg( + Arg::new("dump-users") + .help("Dump the users db to the given file as TOML") + .long("dump-users") + .takes_value(true) + .value_name("FILE") + .value_hint(ValueHint::AnyPath) + .default_missing_value("users.toml") + .conflicts_with("load")) + .arg( + Arg::new("force") + .help("force ops that may clobber") + .long("force") + ) .arg( Arg::new("load") .help("Load values into the internal databases") @@ -124,7 +138,18 @@ fn main() -> miette::Result<()> { let mut config = config::read(&PathBuf::from_str(configpath).unwrap()).unwrap(); if matches.is_present("dump") { - unimplemented!() + return Err(miette::miette!("DB Dumping is currently not implemented, except for the users db, using `--dump-users`")); + } else if matches.is_present("dump-users") { + let bffh = Diflouroborane::new(config)?; + + let number = bffh.users.dump_file( + matches.value_of("dump-users").unwrap(), + matches.is_present("force"), + )?; + + tracing::info!("successfully dumped {} users", number); + + return Ok(()); } else if matches.is_present("load") { let bffh = Diflouroborane::new(config)?;