diff --git a/debian/control b/debian/control index 263427d9..d38f1206 100644 --- a/debian/control +++ b/debian/control @@ -62,6 +62,7 @@ Build-Depends: debhelper (>= 12), librust-proxmox-schema-1+default-dev, librust-proxmox-schema-1+upid-api-impl-dev, librust-proxmox-section-config-1+default-dev, + librust-proxmox-shared-memory-dev, librust-proxmox-tfa-1+default-dev, librust-proxmox-tfa-1+u2f-dev, librust-proxmox-time-1+default-dev, diff --git a/pbs-config/Cargo.toml b/pbs-config/Cargo.toml index f98885a5..08f9e270 100644 --- a/pbs-config/Cargo.toml +++ b/pbs-config/Cargo.toml @@ -23,6 +23,7 @@ proxmox-router = { version = "1.1", default-features = false } proxmox-schema = "1" proxmox-section-config = "1" proxmox-time = "1" +proxmox-shared-memory = "0.1.0" pbs-api-types = { path = "../pbs-api-types" } pbs-buildcfg = { path = "../pbs-buildcfg" } diff --git a/pbs-config/src/cached_user_info.rs b/pbs-config/src/cached_user_info.rs index 495fdd12..24390f50 100644 --- a/pbs-config/src/cached_user_info.rs +++ b/pbs-config/src/cached_user_info.rs @@ -12,7 +12,7 @@ use proxmox_time::epoch_i64; use pbs_api_types::{Authid, Userid, User, ApiToken, ROLE_ADMIN}; use crate::acl::{AclTree, ROLE_NAMES}; -use crate::memcom::Memcom; +use crate::ConfigVersionCache; /// Cache User/Group/Token/Acl configuration data for fast permission tests pub struct CachedUserInfo { @@ -38,8 +38,8 @@ impl CachedUserInfo { pub fn new() -> Result, Error> { let now = epoch_i64(); - let memcom = Memcom::new()?; - let user_cache_generation = memcom.user_cache_generation(); + let version_cache = ConfigVersionCache::new()?; + let user_cache_generation = version_cache.user_cache_generation(); { // limit scope let cache = CACHED_CONFIG.read().unwrap(); diff --git a/pbs-config/src/config_version_cache.rs b/pbs-config/src/config_version_cache.rs new file mode 100644 index 00000000..76ba84e9 --- /dev/null +++ b/pbs-config/src/config_version_cache.rs @@ -0,0 +1,121 @@ +use std::path::Path; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::mem::MaybeUninit; + +use anyhow::{bail, Error}; +use once_cell::sync::OnceCell; +use nix::sys::stat::Mode; + +use proxmox::tools::fs::{create_path, CreateOptions}; + +// openssl::sha::sha256(b"Proxmox Backup ConfigVersionCache v1.0")[0..8]; +pub const PROXMOX_BACKUP_CONFIG_VERSION_CACHE_MAGIC_1_0: [u8; 8] = [25, 198, 168, 230, 154, 132, 143, 131]; + +const FILE_PATH: &str = pbs_buildcfg::rundir!("/shmem/config-versions"); + +use proxmox_shared_memory::*; + +#[derive(Debug)] +#[repr(C)] +struct ConfigVersionCacheData { + magic: [u8; 8], + // User (user.cfg) cache generation/version. + user_cache_generation: AtomicUsize, + // Traffic control (traffic-control.cfg) generation/version. + traffic_control_generation: AtomicUsize, + + // Add further atomics here (and reduce padding size) + + padding: [u8; 4096 - 3*8], +} + + +impl Init for ConfigVersionCacheData { + fn initialize(this: &mut MaybeUninit) { + unsafe { + let me = &mut *this.as_mut_ptr(); + me.magic = PROXMOX_BACKUP_CONFIG_VERSION_CACHE_MAGIC_1_0; + } + } + + fn check_type_magic(this: &MaybeUninit) -> Result<(), Error> { + unsafe { + let me = &*this.as_ptr(); + if me.magic != PROXMOX_BACKUP_CONFIG_VERSION_CACHE_MAGIC_1_0 { + bail!("ConfigVersionCache: wrong magic number"); + } + Ok(()) + } + } +} + + +pub struct ConfigVersionCache { + shmem: SharedMemory +} + +static INSTANCE: OnceCell> = OnceCell::new(); + +impl ConfigVersionCache { + + /// Open the memory based communication channel singleton. + pub fn new() -> Result, Error> { + INSTANCE.get_or_try_init(Self::open).map(Arc::clone) + } + + // Actual work of `new`: + fn open() -> Result, Error> { + let user = crate::backup_user()?; + + let dir_opts = CreateOptions::new() + .perm(Mode::from_bits_truncate(0o770)) + .owner(user.uid) + .group(user.gid); + + let file_path = Path::new(FILE_PATH); + let dir_path = file_path.parent().unwrap(); + + create_path( + dir_path, + Some(dir_opts.clone()), + Some(dir_opts))?; + + let file_opts = CreateOptions::new() + .perm(Mode::from_bits_truncate(0o660)) + .owner(user.uid) + .group(user.gid); + + let shmem: SharedMemory = + SharedMemory::open(file_path, file_opts)?; + + Ok(Arc::new(Self { shmem })) + } + + /// Returns the user cache generation number. + pub fn user_cache_generation(&self) -> usize { + self.shmem.data() + .user_cache_generation.load(Ordering::Acquire) + } + + /// Increase the user cache generation number. + pub fn increase_user_cache_generation(&self) { + self.shmem.data() + .user_cache_generation + .fetch_add(1, Ordering::AcqRel); + } + + /// Returns the traffic control generation number. + pub fn traffic_control_generation(&self) -> usize { + self.shmem.data() + .traffic_control_generation.load(Ordering::Acquire) + } + + /// Increase the traffic control generation number. + pub fn increase_traffic_control_generation(&self) { + self.shmem.data() + .traffic_control_generation + .fetch_add(1, Ordering::AcqRel); + } + +} diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs index bc3b19f0..ba1289ed 100644 --- a/pbs-config/src/lib.rs +++ b/pbs-config/src/lib.rs @@ -16,7 +16,8 @@ pub mod traffic_control; pub mod user; pub mod verify; -pub mod memcom; +mod config_version_cache; +pub use config_version_cache::ConfigVersionCache; use anyhow::{format_err, Error}; diff --git a/pbs-config/src/memcom.rs b/pbs-config/src/memcom.rs deleted file mode 100644 index 7b82798b..00000000 --- a/pbs-config/src/memcom.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Memory based communication channel between proxy & daemon for things such as cache -//! invalidation. - -use std::os::unix::io::AsRawFd; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; - -use anyhow::Error; -use nix::fcntl::OFlag; -use nix::sys::mman::{MapFlags, ProtFlags}; -use nix::sys::stat::Mode; -use once_cell::sync::OnceCell; - -use proxmox::tools::fs::CreateOptions; -use proxmox::tools::mmap::Mmap; - -/// In-memory communication channel. -pub struct Memcom { - mmap: Mmap, -} - -#[repr(C)] -struct Head { - // User (user.cfg) cache generation/version. - user_cache_generation: AtomicUsize, - // Traffic control (traffic-control.cfg) generation/version. - traffic_control_generation: AtomicUsize, -} - -static INSTANCE: OnceCell> = OnceCell::new(); - -const MEMCOM_FILE_PATH: &str = pbs_buildcfg::rundir!("/proxmox-backup-memcom"); -const EMPTY_PAGE: [u8; 4096] = [0u8; 4096]; - -impl Memcom { - /// Open the memory based communication channel singleton. - pub fn new() -> Result, Error> { - INSTANCE.get_or_try_init(Self::open).map(Arc::clone) - } - - // Actual work of `new`: - fn open() -> Result, Error> { - let user = crate::backup_user()?; - let options = CreateOptions::new() - .perm(Mode::from_bits_truncate(0o660)) - .owner(user.uid) - .group(user.gid); - - let file = proxmox::tools::fs::atomic_open_or_create_file( - MEMCOM_FILE_PATH, - OFlag::O_RDWR | OFlag::O_CLOEXEC, - &EMPTY_PAGE, - options, - true, - )?; - - let mmap = unsafe { - Mmap::::map_fd( - file.as_raw_fd(), - 0, - 4096, - ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, - MapFlags::MAP_SHARED | MapFlags::MAP_NORESERVE | MapFlags::MAP_POPULATE, - )? - }; - - Ok(Arc::new(Self { mmap })) - } - - // Shortcut to get the mapped `Head` as a `Head`. - fn head(&self) -> &Head { - unsafe { &*(self.mmap.as_ptr() as *const u8 as *const Head) } - } - - /// Returns the user cache generation number. - pub fn user_cache_generation(&self) -> usize { - self.head().user_cache_generation.load(Ordering::Acquire) - } - - /// Increase the user cache generation number. - pub fn increase_user_cache_generation(&self) { - self.head() - .user_cache_generation - .fetch_add(1, Ordering::AcqRel); - } - - /// Returns the traffic control generation number. - pub fn traffic_control_generation(&self) -> usize { - self.head().traffic_control_generation.load(Ordering::Acquire) - } - - /// Increase the traffic control generation number. - pub fn increase_traffic_control_generation(&self) { - self.head() - .traffic_control_generation - .fetch_add(1, Ordering::AcqRel); - } -} diff --git a/pbs-config/src/traffic_control.rs b/pbs-config/src/traffic_control.rs index 58015b27..ec0f5dc4 100644 --- a/pbs-config/src/traffic_control.rs +++ b/pbs-config/src/traffic_control.rs @@ -10,7 +10,7 @@ use pbs_api_types::{TrafficControlRule, TRAFFIC_CONTROL_ID_SCHEMA}; use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin}; -use crate::memcom::Memcom; +use crate::ConfigVersionCache; use crate::{open_backup_lockfile, replace_backup_config, BackupLockGuard}; lazy_static! { @@ -57,10 +57,10 @@ pub fn save_config(config: &SectionConfigData) -> Result<(), Error> { let raw = CONFIG.write(TRAFFIC_CONTROL_CFG_FILENAME, &config)?; replace_backup_config(TRAFFIC_CONTROL_CFG_FILENAME, raw.as_bytes())?; - // increase traffic control generation + // increase traffic control version // We use this in TrafficControlCache - let memcom = Memcom::new()?; - memcom.increase_traffic_control_generation(); + let version_cache = ConfigVersionCache::new()?; + version_cache.increase_traffic_control_generation(); Ok(()) } diff --git a/pbs-config/src/user.rs b/pbs-config/src/user.rs index db66ab7d..0df0a245 100644 --- a/pbs-config/src/user.rs +++ b/pbs-config/src/user.rs @@ -11,7 +11,7 @@ use pbs_api_types::{ Authid, Userid, ApiToken, User, }; -use crate::memcom::Memcom; +use crate::ConfigVersionCache; use crate::{open_backup_lockfile, replace_backup_config, BackupLockGuard}; @@ -120,10 +120,10 @@ pub fn save_config(config: &SectionConfigData) -> Result<(), Error> { let raw = CONFIG.write(USER_CFG_FILENAME, &config)?; replace_backup_config(USER_CFG_FILENAME, raw.as_bytes())?; - // increase user cache generation + // increase user version // We use this in CachedUserInfo - let memcom = Memcom::new()?; - memcom.increase_user_cache_generation(); + let version_cache = ConfigVersionCache::new()?; + version_cache.increase_user_cache_generation(); Ok(()) } diff --git a/src/cached_traffic_control.rs b/src/cached_traffic_control.rs index af91f9bf..9c7387d6 100644 --- a/src/cached_traffic_control.rs +++ b/src/cached_traffic_control.rs @@ -14,7 +14,7 @@ use proxmox_time::TmEditor; use pbs_api_types::TrafficControlRule; -use pbs_config::memcom::Memcom; +use pbs_config::ConfigVersionCache; struct ParsedTcRule { config: TrafficControlRule, // original rule config @@ -97,15 +97,15 @@ impl TrafficControlCache { } pub fn reload(&mut self, now: i64) { - let memcom = match Memcom::new() { - Ok(memcom) => memcom, + let version_cache = match ConfigVersionCache::new() { + Ok(cache) => cache, Err(err) => { - log::error!("TrafficControlCache::reload failed in Memcom::new: {}", err); + log::error!("TrafficControlCache::reload failed in ConfigVersionCache::new: {}", err); return; } }; - let traffic_control_generation = memcom.traffic_control_generation(); + let traffic_control_generation = version_cache.traffic_control_generation(); if (self.last_update != 0) && (traffic_control_generation == self.last_traffic_control_generation) &&