From a0765714706e6693318f6f77905a1e2a9206a27a Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 4 Dec 2020 15:42:32 +0100 Subject: [PATCH] tape support: add drive configuration --- src/api2/types/mod.rs | 4 ++ src/config.rs | 1 + src/config/drive.rs | 145 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 src/config/drive.rs diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs index b347fc29..9d753f86 100644 --- a/src/api2/types/mod.rs +++ b/src/api2/types/mod.rs @@ -20,6 +20,10 @@ pub use userid::Userid; pub use userid::Authid; pub use userid::{PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA, PROXMOX_GROUP_ID_SCHEMA}; +mod tape; +pub use tape::*; + + // File names: may not contain slashes, may not start with "." pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| { if name.starts_with('.') { diff --git a/src/config.rs b/src/config.rs index 6f19da7c..91c85e53 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,6 +24,7 @@ pub mod sync; pub mod token_shadow; pub mod user; pub mod verify; +pub mod drive; /// Check configuration directory permissions /// diff --git a/src/config/drive.rs b/src/config/drive.rs new file mode 100644 index 00000000..c56bd36a --- /dev/null +++ b/src/config/drive.rs @@ -0,0 +1,145 @@ +use std::collections::HashMap; + +use anyhow::{bail, Error}; +use lazy_static::lazy_static; + +use proxmox::{ + api::{ + schema::*, + section_config::{ + SectionConfig, + SectionConfigData, + SectionConfigPlugin, + }, + }, + tools::fs::{ + open_file_locked, + replace_file, + CreateOptions, + }, +}; + +use crate::{ + api2::types::{ + DRIVE_ID_SCHEMA, + VirtualTapeDrive, + LinuxTapeDrive, + ScsiTapeChanger, + }, +}; + +lazy_static! { + pub static ref CONFIG: SectionConfig = init(); +} + + +fn init() -> SectionConfig { + let mut config = SectionConfig::new(&DRIVE_ID_SCHEMA); + + let obj_schema = match VirtualTapeDrive::API_SCHEMA { + Schema::Object(ref obj_schema) => obj_schema, + _ => unreachable!(), + }; + let plugin = SectionConfigPlugin::new("virtual".to_string(), Some("name".to_string()), obj_schema); + config.register_plugin(plugin); + + let obj_schema = match LinuxTapeDrive::API_SCHEMA { + Schema::Object(ref obj_schema) => obj_schema, + _ => unreachable!(), + }; + let plugin = SectionConfigPlugin::new("linux".to_string(), Some("name".to_string()), obj_schema); + config.register_plugin(plugin); + + let obj_schema = match ScsiTapeChanger::API_SCHEMA { + Schema::Object(ref obj_schema) => obj_schema, + _ => unreachable!(), + }; + let plugin = SectionConfigPlugin::new("changer".to_string(), Some("name".to_string()), obj_schema); + config.register_plugin(plugin); + config +} + +pub const DRIVE_CFG_FILENAME: &str = "/etc/proxmox-backup/tape.cfg"; +pub const DRIVE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.tape.lck"; + +pub fn lock() -> Result { + open_file_locked(DRIVE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true) +} + +pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> { + + let content = proxmox::tools::fs::file_read_optional_string(DRIVE_CFG_FILENAME)?; + let content = content.unwrap_or(String::from("")); + + let digest = openssl::sha::sha256(content.as_bytes()); + let data = CONFIG.parse(DRIVE_CFG_FILENAME, &content)?; + Ok((data, digest)) +} + +pub fn save_config(config: &SectionConfigData) -> Result<(), Error> { + let raw = CONFIG.write(DRIVE_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(DRIVE_CFG_FILENAME, raw.as_bytes(), options)?; + + Ok(()) +} + +pub fn check_drive_exists(config: &SectionConfigData, drive: &str) -> Result<(), Error> { + match config.sections.get(drive) { + Some((section_type, _)) => { + if !(section_type == "linux" || section_type == "virtual") { + bail!("Entry '{}' exists, but is not a tape drive", drive); + } + } + None => bail!("Drive '{}' does not exist", drive), + } + Ok(()) +} + + +// shell completion helper + +/// List all drive names +pub fn complete_drive_name(_arg: &str, _param: &HashMap) -> Vec { + match config() { + Ok((data, _digest)) => data.sections.iter() + .map(|(id, _)| id.to_string()) + .collect(), + Err(_) => return vec![], + } +} + +/// List Linux tape drives +pub fn complete_linux_drive_name(_arg: &str, _param: &HashMap) -> Vec { + match config() { + Ok((data, _digest)) => data.sections.iter() + .filter(|(_id, (section_type, _))| { + section_type == "linux" + }) + .map(|(id, _)| id.to_string()) + .collect(), + Err(_) => return vec![], + } +} + +/// List Scsi tape changer names +pub fn complete_changer_name(_arg: &str, _param: &HashMap) -> Vec { + match config() { + Ok((data, _digest)) => data.sections.iter() + .filter(|(_id, (section_type, _))| { + section_type == "changer" + }) + .map(|(id, _)| id.to_string()) + .collect(), + Err(_) => return vec![], + } +}