summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormdecimus <mauro@stalw.art>2024-09-20 09:59:10 +0200
committermdecimus <mauro@stalw.art>2024-09-20 09:59:10 +0200
commit58351bdcad696e1e1ada6da114ec3f9f3dcb135b (patch)
treefd0dfe0b1b76f6d925dbbaee5d9f58f90d26f4da
parent39d0a168d480eb5b94bd031dd28fc4ba03d9a42e (diff)
Support specifying which data types to export (closes #497)
-rw-r--r--crates/common/src/manager/backup.rs120
-rw-r--r--crates/common/src/manager/boot.rs13
2 files changed, 111 insertions, 22 deletions
diff --git a/crates/common/src/manager/backup.rs b/crates/common/src/manager/backup.rs
index 378474d8..c4f3e509 100644
--- a/crates/common/src/manager/backup.rs
+++ b/crates/common/src/manager/backup.rs
@@ -42,7 +42,7 @@ pub(super) enum Op {
KeyValue((Vec<u8>, Vec<u8>)),
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub(super) enum Family {
Property = 0,
FtsIndex = 1,
@@ -61,30 +61,61 @@ pub(super) enum Family {
type TaskHandle = (tokio::task::JoinHandle<()>, std::thread::JoinHandle<()>);
+#[derive(Debug, Default, PartialEq, Eq)]
+pub struct BackupParams {
+ dest: PathBuf,
+ families: AHashSet<Family>,
+}
+
impl Core {
- pub async fn backup(&self, dest: PathBuf) {
- if !dest.exists() {
- std::fs::create_dir_all(&dest).failed("Failed to create backup directory");
- } else if !dest.is_dir() {
- eprintln!("Backup destination {dest:?} is not a directory.");
+ pub async fn backup(&self, params: BackupParams) {
+ if !params.dest.exists() {
+ std::fs::create_dir_all(&params.dest).failed("Failed to create backup directory");
+ } else if !params.dest.is_dir() {
+ eprintln!("Backup destination {:?} is not a directory.", params.dest);
std::process::exit(1);
}
let mut sync_handles = Vec::new();
for (async_handle, sync_handle) in [
- self.backup_properties(&dest),
- self.backup_fts_index(&dest),
- self.backup_acl(&dest),
- self.backup_blob(&dest),
- self.backup_config(&dest),
- self.backup_lookup(&dest),
- self.backup_directory(&dest),
- self.backup_queue(&dest),
- self.backup_index(&dest),
- self.backup_bitmaps(&dest),
- self.backup_logs(&dest),
- ] {
+ params
+ .has_family(Family::Property)
+ .then(|| self.backup_properties(&params.dest)),
+ params
+ .has_family(Family::FtsIndex)
+ .then(|| self.backup_fts_index(&params.dest)),
+ params
+ .has_family(Family::Acl)
+ .then(|| self.backup_acl(&params.dest)),
+ params
+ .has_family(Family::Blob)
+ .then(|| self.backup_blob(&params.dest)),
+ params
+ .has_family(Family::Config)
+ .then(|| self.backup_config(&params.dest)),
+ params
+ .has_family(Family::LookupValue)
+ .then(|| self.backup_lookup(&params.dest)),
+ params
+ .has_family(Family::Directory)
+ .then(|| self.backup_directory(&params.dest)),
+ params
+ .has_family(Family::Queue)
+ .then(|| self.backup_queue(&params.dest)),
+ params
+ .has_family(Family::Index)
+ .then(|| self.backup_index(&params.dest)),
+ params
+ .has_family(Family::Bitmap)
+ .then(|| self.backup_bitmaps(&params.dest)),
+ params
+ .has_family(Family::Log)
+ .then(|| self.backup_logs(&params.dest)),
+ ]
+ .into_iter()
+ .flatten()
+ {
async_handle.await.failed("Task failed");
sync_handles.push(sync_handle);
}
@@ -1133,6 +1164,59 @@ impl DeserializeBytes for &[u8] {
}
}
+impl BackupParams {
+ pub fn new(dest: PathBuf) -> Self {
+ let mut params = Self {
+ dest,
+ families: AHashSet::new(),
+ };
+
+ if let Ok(families) = std::env::var("EXPORT_TYPES") {
+ params.parse_families(&families);
+ }
+
+ params
+ }
+
+ fn parse_families(&mut self, families: &str) {
+ for family in families.split(',') {
+ let family = family.trim();
+ match Family::parse(family) {
+ Ok(family) => {
+ self.families.insert(family);
+ }
+ Err(err) => {
+ eprintln!("Backup failed: {err}.");
+ std::process::exit(1);
+ }
+ }
+ }
+ }
+
+ fn has_family(&self, family: Family) -> bool {
+ self.families.is_empty() || self.families.contains(&family)
+ }
+}
+
+impl Family {
+ pub fn parse(family: &str) -> Result<Self, String> {
+ match family {
+ "property" => Ok(Family::Property),
+ "fts_index" => Ok(Family::FtsIndex),
+ "acl" => Ok(Family::Acl),
+ "blob" => Ok(Family::Blob),
+ "config" => Ok(Family::Config),
+ "lookup" => Ok(Family::LookupValue),
+ "directory" => Ok(Family::Directory),
+ "queue" => Ok(Family::Queue),
+ "index" => Ok(Family::Index),
+ "bitmap" => Ok(Family::Bitmap),
+ "log" => Ok(Family::Log),
+ _ => Err(format!("Unknown family {}", family)),
+ }
+ }
+}
+
struct RawBytes(Vec<u8>);
impl Deserialize for RawBytes {
diff --git a/crates/common/src/manager/boot.rs b/crates/common/src/manager/boot.rs
index 26493213..b11b5988 100644
--- a/crates/common/src/manager/boot.rs
+++ b/crates/common/src/manager/boot.rs
@@ -23,6 +23,7 @@ use crate::{
};
use super::{
+ backup::BackupParams,
config::{ConfigManager, Patterns},
WEBADMIN_KEY,
};
@@ -33,7 +34,10 @@ pub struct BootManager {
pub servers: Servers,
}
-const HELP: &str = r#"Stalwart Mail Server
+const HELP: &str = concat!(
+ "Stalwart Mail Server v",
+ env!("CARGO_PKG_VERSION"),
+ r#"
Usage: stalwart-mail [OPTIONS]
@@ -44,11 +48,12 @@ Options:
-I, --init <PATH> Initialize a new server at a specific path
-h, --help Print help
-V, --version Print version
-"#;
+"#
+);
#[derive(PartialEq, Eq)]
enum ImportExport {
- Export(PathBuf),
+ Export(BackupParams),
Import(PathBuf),
None,
}
@@ -89,7 +94,7 @@ impl BootManager {
std::process::exit(0);
}
("export" | "e", Some(value)) => {
- import_export = ImportExport::Export(value.into());
+ import_export = ImportExport::Export(BackupParams::new(value.into()));
}
("import" | "i", Some(value)) => {
import_export = ImportExport::Import(value.into());