]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/config/remotes.rs: implement SectionConfig for remote hosts
authorDietmar Maurer <dietmar@proxmox.com>
Thu, 9 Jan 2020 13:49:40 +0000 (14:49 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Thu, 9 Jan 2020 13:51:02 +0000 (14:51 +0100)
src/config.rs
src/config/remotes.rs [new file with mode: 0644]

index 71052d23dcabcd7789aaf886c9b592bdfdabfd53..ee89d5fc495164bf8d60f650481e4eca15324443 100644 (file)
@@ -16,6 +16,7 @@ use proxmox::tools::try_block;
 use crate::buildcfg;
 
 pub mod datastore;
+pub mod remotes;
 
 /// Check configuration directory permissions
 ///
diff --git a/src/config/remotes.rs b/src/config/remotes.rs
new file mode 100644 (file)
index 0000000..83bf9b6
--- /dev/null
@@ -0,0 +1,127 @@
+use failure::*;
+use lazy_static::lazy_static;
+use std::collections::HashMap;
+use serde::Deserialize;
+
+use proxmox::api::{api, schema::*};
+
+use proxmox::tools::{fs::replace_file, fs::CreateOptions};
+
+use crate::section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
+
+lazy_static! {
+    static ref CONFIG: SectionConfig = init();
+}
+
+// fixme: define better schemas
+
+const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.")
+    .min_length(3)
+    .schema();
+
+const COMMENT_SCHEMA: Schema = StringSchema::new("Comment").schema();
+const REMOTE_HOST_SCHEMA: Schema = StringSchema::new("Host IP address or DNS name.").schema();
+const REMOTE_USERID_SCHEMA: Schema = StringSchema::new("User ID").schema();
+const REMOTE_PASSWORD_SCHEMA: Schema = StringSchema::new("Password or auth token.").schema();
+
+#[api(
+    properties: {
+        comment: {
+            optional: true,
+            schema: COMMENT_SCHEMA,
+        },
+        host: {
+            schema: REMOTE_HOST_SCHEMA,
+        },
+        userid: {
+            schema: REMOTE_USERID_SCHEMA,
+        },
+        password: {
+            schema: REMOTE_PASSWORD_SCHEMA,
+        },
+    }
+)]
+#[derive(Deserialize)]
+/// Remote properties.
+pub struct Remote {
+    pub comment: Option<String>,
+    pub host: String,
+    pub userid: String,
+    pub password: String,
+}
+
+fn init() -> SectionConfig {
+    let obj_schema = match Remote::API_SCHEMA {
+        Schema::Object(ref obj_schema) => obj_schema,
+        _ => unreachable!(),
+    };
+
+    let plugin = SectionConfigPlugin::new("remote".to_string(), obj_schema);
+    let mut config = SectionConfig::new(&REMOTE_ID_SCHEMA);
+    config.register_plugin(plugin);
+
+    config
+}
+
+const REMOTES_CFG_FILENAME: &str = "/etc/proxmox-backup/remotes.cfg";
+
+pub fn config() -> Result<SectionConfigData, Error> {
+    let content = match std::fs::read_to_string(REMOTES_CFG_FILENAME) {
+        Ok(c) => c,
+        Err(err) => {
+            if err.kind() == std::io::ErrorKind::NotFound {
+                String::from("")
+            } else {
+                bail!("unable to read '{}' - {}", REMOTES_CFG_FILENAME, err);
+            }
+        }
+    };
+
+    CONFIG.parse(REMOTES_CFG_FILENAME, &content)
+}
+
+pub fn lookup(remote: &str) -> Result<Remote, Error> {
+
+    let remotes = config()?;
+
+    let config = match remotes.sections.get(remote) {
+        Some((type_name, config)) => {
+            if type_name != "remote" {
+                bail!("got unexpected type '{}' for remote '{}'", type_name, remote);
+            }
+            config
+        }
+        None => {
+            bail!("no such remote '{}'", remote);
+        }
+    };
+
+    let remote: Remote = serde_json::from_value(config.clone())?;
+
+    Ok(remote)
+}
+
+pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
+    let raw = CONFIG.write(REMOTES_CFG_FILENAME, &config)?;
+
+    let backup_user = crate::backup::backup_user()?;
+    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
+    // set the correct owner/group/permissions while saving file
+    // owner(rw) = root, group(r)= backup
+    let options = CreateOptions::new()
+        .perm(mode)
+        .owner(nix::unistd::ROOT)
+        .group(backup_user.gid);
+
+    replace_file(REMOTES_CFG_FILENAME, raw.as_bytes(), options)?;
+
+    Ok(())
+}
+
+// shell completion helper
+pub fn complete_remote_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
+    match config() {
+        Ok(data) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
+        Err(_) => return vec![],
+    }
+}