From 9b495390d20219ef19c05a2eaf9462dd9f10a4a9 Mon Sep 17 00:00:00 2001 From: Xavier Date: Sun, 14 Jan 2024 20:45:59 -0500 Subject: [PATCH] Structural simplification, rustfmt configuration applied, and snake_case for declared modules within tag vars - For the sake of coherency, there are now four types of containers: Symbolic, Base, Slice, and Aggregate. These names better reflect the associated container types. - Global configuration template is now written to disk prior to instantiation - Inclusion of .rustfmt.toml with formatting applied to source files - Breaking format change: SCREAMING_CASE has been replaced with snake_case for tag variables - Simplified data structure in cache module - InstanceCache type within the cache module replaces 'registered_base', 'registered_dep', and 'registered_root', with 'filter'. - Cleaned up argument parsing match statements in the front-end modules. - Specifying dependencies with a comma deliniation is now supported - Manual updated to reflect argument changes. - query module updated to utilise internal APIs - Some minor improvements to error handling. - Removed redundant calls to --clear-env with bubblewrap. - Scripting no longer outputs ANSI charcodes to unsupported terminals --- .rustfmt.toml | 16 + bin/pacwrap-utils | 2 +- dist/tools/clean.sh | 11 +- dist/tools/runtime.sh | 11 +- pacwrap-agent/build.rs | 4 +- pacwrap-agent/src/agent.rs | 83 ++--- pacwrap-agent/src/error.rs | 21 +- pacwrap-agent/src/main.rs | 15 +- pacwrap-core/build.rs | 4 +- pacwrap-core/src/config.rs | 83 ++--- pacwrap-core/src/config/cache.rs | 137 ++++---- pacwrap-core/src/config/dbus.rs | 8 +- pacwrap-core/src/config/dbus/appindicator.rs | 6 +- pacwrap-core/src/config/dbus/socket.rs | 14 +- pacwrap-core/src/config/dbus/xdg_portal.rs | 8 +- pacwrap-core/src/config/filesystem.rs | 14 +- pacwrap-core/src/config/filesystem/dir.rs | 24 +- pacwrap-core/src/config/filesystem/home.rs | 26 +- pacwrap-core/src/config/filesystem/root.rs | 24 +- pacwrap-core/src/config/filesystem/sys.rs | 41 ++- pacwrap-core/src/config/filesystem/to_home.rs | 94 +++--- pacwrap-core/src/config/filesystem/to_root.rs | 89 +++-- pacwrap-core/src/config/global.rs | 44 +-- pacwrap-core/src/config/init.rs | 40 +-- pacwrap-core/src/config/instance.rs | 151 +++++---- pacwrap-core/src/config/permission.rs | 17 +- pacwrap-core/src/config/permission/dev.rs | 28 +- pacwrap-core/src/config/permission/display.rs | 110 ++++--- pacwrap-core/src/config/permission/env.rs | 49 +-- pacwrap-core/src/config/permission/gpu.rs | 35 +- pacwrap-core/src/config/permission/net.rs | 20 +- pacwrap-core/src/config/permission/none.rs | 26 +- .../src/config/permission/pipewire.rs | 32 +- .../src/config/permission/pulseaudio.rs | 32 +- pacwrap-core/src/config/register.rs | 38 ++- pacwrap-core/src/config/vars.rs | 62 ++-- pacwrap-core/src/constants.rs | 122 +++++-- pacwrap-core/src/error.rs | 35 +- pacwrap-core/src/exec.rs | 235 ++++++++------ pacwrap-core/src/exec/args.rs | 45 +-- pacwrap-core/src/exec/seccomp.rs | 128 ++++---- pacwrap-core/src/exec/utils.rs | 49 +-- pacwrap-core/src/lib.rs | 42 +-- pacwrap-core/src/log.rs | 69 ++-- pacwrap-core/src/sync.rs | 178 +++++----- pacwrap-core/src/sync/event.rs | 16 +- pacwrap-core/src/sync/event/download.rs | 130 ++++---- pacwrap-core/src/sync/event/progress.rs | 102 +++--- pacwrap-core/src/sync/event/query.rs | 20 +- pacwrap-core/src/sync/filesystem.rs | 299 +++++++++-------- pacwrap-core/src/sync/resolver.rs | 47 +-- pacwrap-core/src/sync/resolver_local.rs | 60 ++-- pacwrap-core/src/sync/transaction.rs | 258 +++++++-------- .../src/sync/transaction/aggregator.rs | 149 +++++---- pacwrap-core/src/sync/transaction/commit.rs | 201 +++++++----- pacwrap-core/src/sync/transaction/prepare.rs | 97 +++--- pacwrap-core/src/sync/transaction/stage.rs | 79 +++-- pacwrap-core/src/sync/transaction/uptodate.rs | 29 +- pacwrap-core/src/sync/utils.rs | 116 ++++--- pacwrap-core/src/utils.rs | 43 ++- pacwrap-core/src/utils/arguments.rs | 78 ++--- pacwrap-core/src/utils/prompt.rs | 18 +- pacwrap-core/src/utils/termcontrol.rs | 42 ++- pacwrap/build.rs | 20 +- pacwrap/src/compat.rs | 26 +- pacwrap/src/exec.rs | 304 ++++++++++-------- pacwrap/src/main.rs | 14 +- pacwrap/src/manual.rs | 301 ++++++++--------- pacwrap/src/query.rs | 32 +- pacwrap/src/remove.rs | 100 +++--- pacwrap/src/sync.rs | 276 ++++++++-------- 71 files changed, 2781 insertions(+), 2398 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..209b16b --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,16 @@ +unstable_features = true +indent_style = "Block" +imports_indent = "Block" +imports_layout = "HorizontalVertical" +imports_granularity = "Crate" +brace_style = "PreferSameLine" +match_arm_leading_pipes = "Never" +match_arm_blocks = false +condense_wildcard_suffixes = true +overflow_delimited_expr = false +spaces_around_ranges = true +reorder_imports = true +hard_tabs = false +max_width = 130 +fn_call_width = 120 +chain_width = 90 diff --git a/bin/pacwrap-utils b/bin/pacwrap-utils index 5a39811..1349aeb 100755 --- a/bin/pacwrap-utils +++ b/bin/pacwrap-utils @@ -355,7 +355,7 @@ replicate_instance() { [[ $type != BASE ]] && depend=$(return_dependency) case $type in - ROOT) params+="r";; + ROOT) params+="a";; BASE) params+="b";; DEP) params+="s";; LINK) ln -s "$INSTANCE_ROOT_DIR/$depend" "$INSTANCE_ROOT_DIR/$instance" diff --git a/dist/tools/clean.sh b/dist/tools/clean.sh index 92b31cc..d35f751 100755 --- a/dist/tools/clean.sh +++ b/dist/tools/clean.sh @@ -18,10 +18,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -BOLD=$(tput bold) -GREEN=$(tput setaf 2) -RED=$(tput setaf 1) -RESET=$(tput sgr0) +if ! [[ -z $COLORTERM ]] || [[ $TERM == "dummy" ]]; then + BOLD=$(tput bold) + GREEN=$(tput setaf 2) + RED=$(tput setaf 1) + RESET=$(tput sgr0) +fi + DIST_RUNTIME="./dist/runtime" DIST_BASE="./dist/pacwrap-base-dist" DIST_REPO="./dist/repo" diff --git a/dist/tools/runtime.sh b/dist/tools/runtime.sh index 8bf1f63..8fe1601 100755 --- a/dist/tools/runtime.sh +++ b/dist/tools/runtime.sh @@ -21,10 +21,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -BOLD=$(tput bold) -RED=$(tput setaf 1) -GREEN=$(tput setaf 2) -RESET=$(tput sgr0) +if ! [[ -z $COLORTERM ]] || [[ $TERM == "dummy" ]]; then + BOLD=$(tput bold) + RED=$(tput setaf 1) + GREEN=$(tput setaf 2) + RESET=$(tput sgr0) +fi + LIB_DIR="/lib" BIN_DIR="/bin" DEST_DIR="./dist/runtime" diff --git a/pacwrap-agent/build.rs b/pacwrap-agent/build.rs index 67592c3..cea9ee9 100644 --- a/pacwrap-agent/build.rs +++ b/pacwrap-agent/build.rs @@ -1,6 +1,6 @@ /* * pacwrap-agent - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -18,7 +18,7 @@ */ fn main() { - if ! cfg!(target_os="linux") || ! cfg!(target_family="unix") { + if !cfg!(target_os = "linux") || !cfg!(target_family = "unix") { panic!("Unsupported build target. Please refer to the documentation for further information.") } } diff --git a/pacwrap-agent/src/agent.rs b/pacwrap-agent/src/agent.rs index ec1d106..b620eee 100644 --- a/pacwrap-agent/src/agent.rs +++ b/pacwrap-agent/src/agent.rs @@ -1,6 +1,6 @@ /* * pacwrap-agent - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -16,57 +16,64 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -use std::{fs::{self, File}, io::ErrorKind::NotFound, os::unix::prelude::FileExt, env}; +use std::{ + env, + fs::{self, File}, + io::ErrorKind::NotFound, + os::unix::prelude::FileExt, +}; use serde::Deserialize; -use pacwrap_core::{err, +use pacwrap_core::{ + config::Global, + err, + sync::{ + self, + event::{ + download::{self, DownloadEvent}, + progress::{self, ProgressEvent}, + query, + }, + transaction::{TransactionHandle, TransactionMetadata, TransactionParameters, TransactionType, MAGIC_NUMBER}, + utils::{erroneous_preparation, erroneous_transaction}, + AlpmConfigData, + SyncError, + }, + utils::{print_warning, read_le_32}, Error, Result, - sync::{self, - SyncError, - AlpmConfigData, - utils::{erroneous_transaction, - erroneous_preparation}, - transaction::{TransactionHandle, - TransactionType, - TransactionMetadata, - TransactionParameters, - MAGIC_NUMBER}, - event::{download::{self, DownloadEvent}, - progress::{self, ProgressEvent}, - query}}, - utils::{print_warning, read_le_32}, config::Global}; +}; use crate::error::AgentError; static AGENT_PARAMS: &'static str = "/mnt/agent_params"; pub fn transact() -> Result<()> { - let mut header_buffer = vec![0; 7]; + let mut header_buffer = vec![0; 7]; let mut file = match File::open(AGENT_PARAMS) { Ok(file) => file, Err(error) => { if let Ok(var) = env::var("SHELL") { - if ! var.is_empty() { + if !var.is_empty() { err!(AgentError::DirectExecution)? } } err!(AgentError::IOError(AGENT_PARAMS, error.kind()))? - }, - }; + } + }; if let Err(error) = file.read_exact_at(&mut header_buffer, 0) { err!(AgentError::IOError(AGENT_PARAMS, error.kind()))? } - + decode_header(&header_buffer)?; let params: TransactionParameters = deserialize(&mut file)?; let config: Global = deserialize(&mut file)?; let alpm_remotes: AlpmConfigData = deserialize(&mut file)?; - let mut metadata: TransactionMetadata = deserialize(&mut file)?; + let mut metadata: TransactionMetadata = deserialize(&mut file)?; let alpm = sync::instantiate_alpm_agent(&config, &alpm_remotes); let mut handle = TransactionHandle::new(&config, alpm, &mut metadata); @@ -74,11 +81,11 @@ pub fn transact() -> Result<()> { } fn conduct_transaction(config: &Global, handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> { - let flags = handle.retrieve_flags(); + let flags = handle.retrieve_flags(); let mode = agent.mode(); let action = agent.action(); let config = config.config(); - let progress = config.progress(); + let pkind = config.progress(); let bytes = agent.bytes(); let files = agent.files(); @@ -86,12 +93,12 @@ fn conduct_transaction(config: &Global, handle: &mut TransactionHandle, agent: T err!(SyncError::InitializationFailure(error.to_string().into()))? } - handle.ignore(true); + handle.ignore(true); - if let TransactionType::Upgrade(upgrade, downgrade, _) = action { + if let TransactionType::Upgrade(upgrade, downgrade, _) = action { if upgrade { handle.alpm().sync_sysupgrade(downgrade).unwrap(); - } + } } handle.prepare(&action, &flags.0.unwrap())?; @@ -100,17 +107,12 @@ fn conduct_transaction(config: &Global, handle: &mut TransactionHandle, agent: T erroneous_preparation(error)? } - let progress_cb = ProgressEvent::new() - .style(progress.0) - .configure(&action); - let download_cb = DownloadEvent::new() - .style(progress.0) - .total(bytes, files) - .configure(&mode, progress.1); + let progress_cb = ProgressEvent::new().style(pkind.0).configure(&action); + let download_cb = DownloadEvent::new().style(pkind.0).total(bytes, files).configure(&mode, pkind.1); handle.alpm().set_question_cb((), query::callback); - handle.alpm().set_progress_cb(progress_cb, progress::callback(&mode, progress.0)); - handle.alpm().set_dl_cb(download_cb, download::callback(progress.1)); + handle.alpm().set_progress_cb(progress_cb, progress::callback(&mode, pkind.0)); + handle.alpm().set_dl_cb(download_cb, download::callback(pkind.1)); if let Err(error) = handle.alpm_mut().trans_commit() { erroneous_transaction(error)? @@ -121,7 +123,8 @@ fn conduct_transaction(config: &Global, handle: &mut TransactionHandle, agent: T if let Err(error) = fs::copy("/etc/ld.so.cache", "/mnt/fs/etc/ld.so.cache") { match error.kind() { - NotFound => (), _ => print_warning(format!("Failed to propagate ld.so.cache: {}", error)), + NotFound => (), + _ => print_warning(format!("Failed to propagate ld.so.cache: {}", error)), } } @@ -139,7 +142,7 @@ fn decode_header(buffer: &Vec) -> Result<()> { } if major.0 != major.1 || minor.0 != minor.1 || patch.0 != patch.1 { - err!(AgentError::InvalidVersion(major.0,minor.0,patch.0,major.1,minor.1,patch.1))?; + err!(AgentError::InvalidVersion(major.0, minor.0, patch.0, major.1, minor.1, patch.1))?; } Ok(()) @@ -148,6 +151,6 @@ fn decode_header(buffer: &Vec) -> Result<()> { fn deserialize Deserialize<'de>>(stdin: &mut File) -> Result { match bincode::deserialize_from::<&mut File, T>(stdin) { Ok(meta) => Ok(meta), - Err(error) => err!(AgentError::DeserializationError(error.as_ref().to_string())) + Err(error) => err!(AgentError::DeserializationError(error.as_ref().to_string())), } } diff --git a/pacwrap-agent/src/error.rs b/pacwrap-agent/src/error.rs index 2db3e17..7811d68 100644 --- a/pacwrap-agent/src/error.rs +++ b/pacwrap-agent/src/error.rs @@ -1,6 +1,6 @@ /* * pacwrap-agent - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,25 +19,30 @@ use std::fmt::{Display, Formatter}; -use pacwrap_core::{ErrorTrait, constants::{RESET, BOLD}}; +use pacwrap_core::{ + constants::{BOLD, RESET}, + ErrorTrait, +}; #[derive(Debug)] pub enum AgentError { DeserializationError(String), - InvalidVersion(u8,u8,u8,u8,u8,u8), + InvalidVersion(u8, u8, u8, u8, u8, u8), InvalidMagic(u32, u32), IOError(&'static str, std::io::ErrorKind), DirectExecution, } impl Display for AgentError { - fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { match self { Self::DirectExecution => write!(fmter, "Direct execution of this binary is unsupported."), Self::InvalidMagic(magic, comparator) => write!(fmter, "Magic mismatch {} != {}", magic, comparator), - Self::InvalidVersion(a, b, c, d, e, f) => write!(fmter, "Version mismatch {}.{}.{} != {}.{}.{}", a, b, c, d, e, f), + Self::InvalidVersion(a, b, c, d, e, f) => { + write!(fmter, "Version mismatch {}.{}.{} != {}.{}.{}", a, b, c, d, e, f) + } Self::DeserializationError(error) => write!(fmter, "Deserilization error: {}", error), - Self::IOError(file, error) => write!(fmter, "'{}{}{}' {}", *BOLD, file, *RESET, error) + Self::IOError(file, error) => write!(fmter, "'{}{}{}' {}", *BOLD, file, *RESET, error), } } } @@ -49,7 +54,7 @@ impl ErrorTrait for AgentError { Self::InvalidVersion(..) => 4, Self::DeserializationError(..) => 3, Self::IOError(..) => 2, - _ => 1 + _ => 1, } - } + } } diff --git a/pacwrap-agent/src/main.rs b/pacwrap-agent/src/main.rs index 458d9cd..1c9e33f 100644 --- a/pacwrap-agent/src/main.rs +++ b/pacwrap-agent/src/main.rs @@ -1,6 +1,6 @@ /* * pacwrap-agent - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,20 +17,25 @@ * along with this program. If not, see . */ -use pacwrap_core::{err, Error, utils::{Arguments, arguments::Operand}}; +use pacwrap_core::{ + err, + utils::{arguments::Operand, Arguments}, + Error, +}; use crate::error::AgentError; -mod error; mod agent; +mod error; fn main() { let arguments = &mut Arguments::new().populate(); let param = arguments.next().unwrap_or_default(); let result = match param { - Operand::Value("transact") => agent::transact(), _ => err!(AgentError::DirectExecution) + Operand::Value("transact") => agent::transact(), + _ => err!(AgentError::DirectExecution), }; - + if let Err(error) = result { error.handle(); } diff --git a/pacwrap-core/build.rs b/pacwrap-core/build.rs index 18685fb..cd5563f 100644 --- a/pacwrap-core/build.rs +++ b/pacwrap-core/build.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -27,7 +27,7 @@ fn dist_repo() -> String { } fn main() { - if ! cfg!(target_os="linux") || ! cfg!(target_family="unix") { + if !cfg!(target_os = "linux") || !cfg!(target_family = "unix") { panic!("Unsupported build target. Please refer to the documentation for further information.") } diff --git a/pacwrap-core/src/config.rs b/pacwrap-core/src/config.rs index 9b2cdae..cd4c1f1 100644 --- a/pacwrap-core/src/config.rs +++ b/pacwrap-core/src/config.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,35 +17,42 @@ * along with this program. If not, see . */ -use std::{fmt::Display, - io::{Write, ErrorKind::NotFound}, - fmt::Formatter, - path::Path, - fs::File}; +use std::{ + fmt::{Display, Formatter}, + fs::File, + io::{ErrorKind::NotFound, Write}, + path::Path, +}; use serde::Serialize; -use crate::{err, impl_error, ErrorKind, error::*, constants::{BOLD, RESET, CONFIG_FILE}}; +use crate::{ + constants::{BOLD, CONFIG_FILE, RESET}, + err, + error::*, + impl_error, + ErrorKind, +}; -pub use self::{cache::InstanceCache, - instance::{Instance, - InstanceHandle, - InstanceType}, - vars::InsVars, - filesystem::{Filesystem, BindError}, - permission::{Permission, PermError}, +pub use self::{ + cache::InstanceCache, dbus::Dbus, - global::{Global, CONFIG}}; + filesystem::{BindError, Filesystem}, + global::{Global, CONFIG}, + instance::{Instance, InstanceHandle, InstanceType}, + permission::{PermError, Permission}, + vars::InsVars, +}; -pub mod vars; -pub mod filesystem; -pub mod permission; -pub mod dbus; pub mod cache; -pub mod instance; -pub mod init; -pub mod register; +pub mod dbus; +pub mod filesystem; pub mod global; +pub mod init; +pub mod instance; +pub mod permission; +pub mod register; +pub mod vars; #[derive(Debug, Clone)] pub enum ConfigError { @@ -61,13 +68,13 @@ impl_error!(ConfigError); impl Display for ConfigError { fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { - Self::Filesystem(module, err) => write!(fmter, "Failed to register filesystem {}: {} ", module, err), - Self::Permission(module, err) => write!(fmter, "Failed to register permission {}: {} ", module, err), + match self { + Self::Filesystem(module, err) => write!(fmter, "Failed to register filesystem '{}': {} ", module, err), + Self::Permission(module, err) => write!(fmter, "Failed to register permission '{}': {} ", module, err), Self::Load(ins, error) => write!(fmter, "Failed to load '{ins}': {error}"), Self::Save(ins, error) => write!(fmter, "Failed to save '{ins}': {error}"), Self::AlreadyExists(ins) => write!(fmter, "Container {}{ins}{} already exists.", *BOLD, *RESET), - Self::ConfigNotFound(ins) => write!(fmter, "Configuration '{}{ins}{}.yml' not found.", *BOLD, *RESET) + Self::ConfigNotFound(ins) => write!(fmter, "Configuration '{}{ins}{}' not found.", *BOLD, *RESET), } } } @@ -79,10 +86,10 @@ impl From for String { } #[inline] -pub fn provide_handle(instance: &str) -> Result { +pub fn provide_handle(instance: &str) -> Result { let vars = InsVars::new(instance); - if ! Path::new(vars.root()).exists() { + if !Path::new(vars.root()).exists() { err!(ErrorKind::InstanceNotFound(instance.into()))? } @@ -94,19 +101,19 @@ pub fn provide_new_handle(instance: &str) -> Result { handle(instance, InsVars::new(instance)) } -fn save(obj: &T, path: &str) -> Result<()> { +fn save(obj: &T, path: &str) -> Result<()> { let mut f = match File::create(Path::new(path)) { Ok(f) => f, - Err(error) => err!(ErrorKind::IOError(path.into(), error.kind()))? + Err(error) => err!(ErrorKind::IOError(path.into(), error.kind()))?, }; let config = match serde_yaml::to_string(&obj) { Ok(file) => file, - Err(error) => err!(ConfigError::Save(path.into(), error.to_string()))? + Err(error) => err!(ConfigError::Save(path.into(), error.to_string()))?, }; - + match write!(f, "{}", config) { Ok(_) => Ok(()), - Err(error) => err!(ErrorKind::IOError(path.into(), error.kind())) + Err(error) => err!(ErrorKind::IOError(path.into(), error.kind())), } } @@ -115,15 +122,15 @@ fn handle<'a>(instance: &str, vars: InsVars<'a>) -> Result> { Ok(file) => { let config = match serde_yaml::from_reader(&file) { Ok(file) => file, - Err(error) => err!(ConfigError::Load(vars.instance().into(), error.to_string()))? + Err(error) => err!(ConfigError::Load(vars.instance().into(), error.to_string()))?, }; Ok(InstanceHandle::new(config, vars)) - }, + } Err(error) => match error.kind() { NotFound => err!(ConfigError::ConfigNotFound(instance.into()))?, _ => err!(ErrorKind::IOError(vars.config_path().into(), error.kind()))?, - } + }, } } @@ -133,11 +140,11 @@ fn config() -> Result { match File::open(*CONFIG_FILE) { Ok(file) => match serde_yaml::from_reader(&file) { Ok(file) => Ok(file), - Err(error) => err!(ConfigError::Load(CONFIG_FILE.to_string(), error.to_string()))? + Err(error) => err!(ConfigError::Load(CONFIG_FILE.to_string(), error.to_string()))?, }, Err(error) => match error.kind() { NotFound => err!(ConfigError::ConfigNotFound(CONFIG_FILE.to_string()))?, _ => err!(ErrorKind::IOError(CONFIG_FILE.to_string(), error.kind()))?, - } + }, } } diff --git a/pacwrap-core/src/config/cache.rs b/pacwrap-core/src/config/cache.rs index ef87993..021ba47 100644 --- a/pacwrap-core/src/config/cache.rs +++ b/pacwrap-core/src/config/cache.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,33 +19,24 @@ use std::{collections::HashMap, fs::read_dir}; -use crate::{err, - ErrorKind, +use crate::{ + config::{self, InstanceHandle}, + constants::DATA_DIR, + err, error::*, - constants::DATA_DIR, - config::{self, InstanceHandle}}; + ErrorKind, +}; -use super::{InsVars, - ConfigError, - Instance, - instance::InstanceType}; +use super::{instance::InstanceType, ConfigError, InsVars, Instance}; pub struct InstanceCache<'a> { instances: HashMap<&'a str, InstanceHandle<'a>>, - registered: Vec<&'a str>, - registered_base: Vec<&'a str>, - registered_dep: Vec<&'a str>, - registered_root: Vec<&'a str> } -impl <'a>InstanceCache<'a> { +impl<'a> InstanceCache<'a> { pub fn new() -> Self { Self { instances: HashMap::new(), - registered: Vec::new(), - registered_base: Vec::new(), - registered_dep: Vec::new(), - registered_root: Vec::new(), } } @@ -57,87 +48,84 @@ impl <'a>InstanceCache<'a> { for dep in deps.iter() { if let None = self.instances.get(dep) { err!(ErrorKind::DependencyNotFound((*dep).into(), ins.into()))? - } + } } let deps = deps.iter().map(|a| (*a).into()).collect(); let handle = match config::provide_new_handle(ins) { - Ok(mut handle) => { - handle.metadata_mut().set(deps, vec!()); - handle - }, + Ok(mut handle) => { + handle.metadata_mut().set(deps, vec![]); + handle + } Err(err) => match err.downcast::() { Ok(error) => match error { ConfigError::ConfigNotFound(_) => { - let vars = InsVars::new(ins); - let cfg = Instance::new(instype, deps, vec!()); - - InstanceHandle::new(cfg, vars) - }, + let vars = InsVars::new(ins); + let cfg = Instance::new(instype, deps, vec![]); + + InstanceHandle::new(cfg, vars) + } _ => Err(err)?, - } + }, _ => Err(err)?, }, }; - Ok(self.register(ins, handle)) + Ok(self.register(ins, handle)) } - fn map(&mut self, ins: &'a str) -> Result<()> { + fn map(&mut self, ins: &'a str) -> Result<()> { if let Some(_) = self.instances.get(ins) { err!(ConfigError::AlreadyExists(ins.to_owned()))? } - Ok(self.register(ins, match config::provide_handle(ins) { - Ok(ins) => ins, - Err(error) => { - error.warn(); - return Ok(()) - } - })) + Ok(self.register( + ins, + match config::provide_handle(ins) { + Ok(ins) => ins, + Err(error) => { + error.warn(); + return Ok(()); + } + }, + )) } fn register(&mut self, ins: &'a str, handle: InstanceHandle<'a>) { - match handle.metadata().container_type() { - InstanceType::BASE => self.registered_base.push(ins), - InstanceType::DEP => self.registered_dep.push(ins), - InstanceType::ROOT => self.registered_root.push(ins), - InstanceType::LINK => return, - } + if let InstanceType::Symbolic = handle.metadata().container_type() { + return; + } self.instances.insert(ins, handle); - self.registered.push(ins); } - pub fn registered(&self) -> &Vec<&'a str> { - &self.registered - } - - pub fn registered_base(&self) -> &Vec<&'a str> { - &self.registered_base - } - - pub fn registered_dep(&self) -> &Vec<&'a str> { - &self.registered_dep + pub fn registered(&self) -> Vec<&'a str> { + self.instances.iter().map(|a| *a.0).collect() } - pub fn registered_root(&self) -> &Vec<&'a str> { - &self.registered_root + pub fn filter(&self, filter: Vec) -> Vec<&'a str> { + self.instances + .iter() + .filter(|a| filter.contains(a.1.metadata().container_type())) + .map(|a| *a.0) + .collect() } pub fn obtain_base_handle(&self) -> Option<&InstanceHandle> { - match self.registered_base.get(0) { - Some(instance) => self.instances.get(instance), None => None, + match self.filter(vec![InstanceType::Base]).get(0) { + Some(instance) => self.instances.get(instance), + None => None, } } - pub fn get_instance(&self, ins: &str) -> Result<&InstanceHandle> { + pub fn get_instance(&self, ins: &str) -> Result<&InstanceHandle> { match self.instances.get(ins) { - Some(ins) => Ok(ins), None => err!(ErrorKind::InstanceNotFound(ins.into())), + Some(ins) => Ok(ins), + None => err!(ErrorKind::InstanceNotFound(ins.into())), } } - pub fn get_instance_option(&self, ins: &str) -> Option<&InstanceHandle> { + pub fn get_instance_option(&self, ins: &str) -> Option<&InstanceHandle> { self.instances.get(ins) } } @@ -156,22 +144,25 @@ pub fn populate<'a>() -> Result> { populate_from(&roots()?) } -fn roots<'a>() -> Result> { +fn roots<'a>() -> Result> { match read_dir(format!("{}/root", *DATA_DIR)) { - Ok(dir) => Ok(dir.filter(|f| match f { - Ok(f) => match f.metadata() { - Ok(meta) => meta.is_dir() | meta.is_symlink(), Err(_) => false - }, - Err(_) => false - }) - .map(|s| match s { + Ok(dir) => Ok(dir + .filter(|f| match f { + Ok(f) => match f.metadata() { + Ok(meta) => meta.is_dir() | meta.is_symlink(), + Err(_) => false, + }, + Err(_) => false, + }) + .map(|s| match s { Ok(f) => match f.file_name().to_str() { - Some(f) => f.to_owned().leak(), None => "", + Some(f) => f.to_owned().leak(), + None => "", }, Err(_) => "", }) - .filter(|e| ! e.is_empty()) - .collect()), + .filter(|e| !e.is_empty()) + .collect()), Err(error) => err!(ErrorKind::IOError(format!("{}/root", *DATA_DIR), error.kind())), } } diff --git a/pacwrap-core/src/config/dbus.rs b/pacwrap-core/src/config/dbus.rs index 8852c51..9525005 100644 --- a/pacwrap-core/src/config/dbus.rs +++ b/pacwrap-core/src/config/dbus.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,13 +19,13 @@ use crate::exec::args::ExecutionArgs; -use dyn_clone::{DynClone, clone_trait_object}; +use dyn_clone::{clone_trait_object, DynClone}; -mod socket; mod appindicator; +mod socket; mod xdg_portal; -#[typetag::serde(tag = "permission")] +#[typetag::serde(tag = "module")] pub trait Dbus: DynClone { fn register(&self, args: &mut ExecutionArgs); } diff --git a/pacwrap-core/src/config/dbus/appindicator.rs b/pacwrap-core/src/config/dbus/appindicator.rs index 53086ba..ead0805 100644 --- a/pacwrap-core/src/config/dbus/appindicator.rs +++ b/pacwrap-core/src/config/dbus/appindicator.rs @@ -3,10 +3,10 @@ use serde::{Deserialize, Serialize}; use crate::{config::Dbus, exec::args::ExecutionArgs}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct APPINDICATOR; +struct AppIndicator; -#[typetag::serde] -impl Dbus for APPINDICATOR { +#[typetag::serde(name = "appindicator")] +impl Dbus for AppIndicator { fn register(&self, args: &mut ExecutionArgs) { args.dbus("broadcast", "org.kde.StatusNotifierWatcher=@/StatusNotifierWatcher"); } diff --git a/pacwrap-core/src/config/dbus/socket.rs b/pacwrap-core/src/config/dbus/socket.rs index 98cd6d8..c612155 100644 --- a/pacwrap-core/src/config/dbus/socket.rs +++ b/pacwrap-core/src/config/dbus/socket.rs @@ -3,22 +3,20 @@ use serde::{Deserialize, Serialize}; use crate::{config::Dbus, exec::args::ExecutionArgs}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct SOCKET { +struct Socket { socket: String, - address: Vec + address: Vec, } -#[typetag::serde] -impl Dbus for SOCKET { +#[typetag::serde(name = "socket")] +impl Dbus for Socket { fn register(&self, args: &mut ExecutionArgs) { match self.socket.to_lowercase().as_str() { - p if p == "call" || p == "talk" || p == "see" || p == "own" || p == "broadcast" => { + p if p == "call" || p == "talk" || p == "see" || p == "own" || p == "broadcast" => for sock in self.address.iter() { args.dbus(p, sock); - } - }, + }, &_ => {} } } } - diff --git a/pacwrap-core/src/config/dbus/xdg_portal.rs b/pacwrap-core/src/config/dbus/xdg_portal.rs index 0e5b4e4..b1c70bc 100644 --- a/pacwrap-core/src/config/dbus/xdg_portal.rs +++ b/pacwrap-core/src/config/dbus/xdg_portal.rs @@ -5,11 +5,11 @@ use serde::{Deserialize, Serialize}; use crate::{config::Dbus, exec::args::ExecutionArgs}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct XDG_PORTAL; +struct XdgPortal; -#[typetag::serde] -impl Dbus for XDG_PORTAL { - fn register(&self, args: &mut ExecutionArgs) { +#[typetag::serde(name = "xdg_portal")] +impl Dbus for XdgPortal { + fn register(&self, args: &mut ExecutionArgs) { args.dbus("call", "org.freedesktop.portal.*=*"); args.dbus("broadcast", "org.freedesktop.portal.*=@/org/freedesktop/portal/*"); } diff --git a/pacwrap-core/src/config/filesystem.rs b/pacwrap-core/src/config/filesystem.rs index f6dc286..6e224a4 100644 --- a/pacwrap-core/src/config/filesystem.rs +++ b/pacwrap-core/src/config/filesystem.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,22 +19,21 @@ use std::fmt::{Display, Formatter}; -use crate::exec::args::ExecutionArgs; -use crate::config::InsVars; +use crate::{config::InsVars, exec::args::ExecutionArgs}; -use dyn_clone::{DynClone, clone_trait_object}; +use dyn_clone::{clone_trait_object, DynClone}; +mod dir; pub mod home; pub mod root; +mod sys; mod to_home; mod to_root; -mod dir; -mod sys; pub enum Condition { Success, SuccessWarn(String), - Nothing + Nothing, } #[derive(Debug, Clone)] @@ -48,7 +47,6 @@ impl Display for BindError { match self { Self::Fail(error) => write!(fmter, "{}", error), Self::Warn(error) => write!(fmter, "{}", error), - } } } diff --git a/pacwrap-core/src/config/filesystem/dir.rs b/pacwrap-core/src/config/filesystem/dir.rs index 5d1f797..08d6e6b 100644 --- a/pacwrap-core/src/config/filesystem/dir.rs +++ b/pacwrap-core/src/config/filesystem/dir.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,23 +19,27 @@ use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::InsVars, - config::filesystem::{Filesystem, BindError}}; +use crate::{ + config::{ + filesystem::{BindError, Filesystem}, + InsVars, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DIR { +pub struct Dir { #[serde(default)] - path: Vec + path: Vec, } -#[typetag::serde] -impl Filesystem for DIR { +#[typetag::serde(name = "dir")] +impl Filesystem for Dir { fn check(&self, _vars: &InsVars) -> Result<(), BindError> { if self.path.len() == 0 { Err(BindError::Fail(format!("Path not specified.")))? } - + Ok(()) } @@ -46,6 +50,6 @@ impl Filesystem for DIR { } fn module(&self) -> &'static str { - "DIR" + "dir" } } diff --git a/pacwrap-core/src/config/filesystem/home.rs b/pacwrap-core/src/config/filesystem/home.rs index 4f3bf3a..0b27c7d 100644 --- a/pacwrap-core/src/config/filesystem/home.rs +++ b/pacwrap-core/src/config/filesystem/home.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,18 +21,22 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::InsVars, - config::filesystem::{Filesystem, BindError}}; +use crate::{ + config::{ + filesystem::{BindError, Filesystem}, + InsVars, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HOME; +pub struct Home; -#[typetag::serde] -impl Filesystem for HOME { +#[typetag::serde(name = "home")] +impl Filesystem for Home { fn check(&self, vars: &InsVars) -> Result<(), BindError> { - if ! Path::new(vars.home()).exists() { - Err(BindError::Fail(format!("INSTANCE_HOME not found.")))? + if !Path::new(vars.home()).exists() { + Err(BindError::Fail(format!("Specified home directory not found.")))? } Ok(()) } @@ -40,10 +44,10 @@ impl Filesystem for HOME { fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) { args.bind(vars.home(), vars.home_mount()); args.env("HOME", vars.home_mount()); - args.env("USER", vars.user()); + args.env("USER", vars.user()); } fn module(&self) -> &'static str { - "HOME" + "home" } } diff --git a/pacwrap-core/src/config/filesystem/root.rs b/pacwrap-core/src/config/filesystem/root.rs index fd593d9..8f6080f 100644 --- a/pacwrap-core/src/config/filesystem/root.rs +++ b/pacwrap-core/src/config/filesystem/root.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,23 +21,27 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::InsVars, - config::filesystem::{Filesystem, BindError}}; +use crate::{ + config::{ + filesystem::{BindError, Filesystem}, + InsVars, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ROOT; +pub struct Root; -#[typetag::serde] -impl Filesystem for ROOT { +#[typetag::serde(name = "root")] +impl Filesystem for Root { fn check(&self, vars: &InsVars) -> Result<(), BindError> { - if ! Path::new(vars.root()).exists() { + if !Path::new(vars.root()).exists() { Err(BindError::Fail(format!("Container {} not found. ", vars.instance())))? } Ok(()) } - fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) { + fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) { args.robind(format!("{}/usr", vars.root()), "/usr"); args.robind(format!("{}/etc", vars.root()), "/etc"); args.symlink("/usr/lib", "/lib"); @@ -47,6 +51,6 @@ impl Filesystem for ROOT { } fn module(&self) -> &'static str { - "ROOT" + "root" } } diff --git a/pacwrap-core/src/config/filesystem/sys.rs b/pacwrap-core/src/config/filesystem/sys.rs index fbb48c6..f101186 100644 --- a/pacwrap-core/src/config/filesystem/sys.rs +++ b/pacwrap-core/src/config/filesystem/sys.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -16,41 +16,44 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::InsVars, - config::filesystem::{Filesystem, BindError}}; +use crate::{ + config::{ + filesystem::{BindError, Filesystem}, + InsVars, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct SYSFS { - #[serde(skip_serializing_if = "is_default_path", default = "default_path")] - path: Vec +struct System { + #[serde(skip_serializing_if = "is_default_path", default = "default_path")] + path: Vec, } -#[typetag::serde] -impl Filesystem for SYSFS { - fn check(&self, _vars: &InsVars) -> Result<(), BindError> { +#[typetag::serde(name = "sysfs")] +impl Filesystem for System { + fn check(&self, _vars: &InsVars) -> Result<(), BindError> { for dir in self.path.iter() { - if ! Path::new(&format!("/sys/{}",dir)).exists() { + if !Path::new(&format!("/sys/{}", dir)).exists() { Err(BindError::Fail(format!("/sys/{} is inaccessible.", dir)))? } } Ok(()) } - - fn register(&self, args: &mut ExecutionArgs, _: &InsVars) { - for dir in self.path.iter() { + + fn register(&self, args: &mut ExecutionArgs, _: &InsVars) { + for dir in self.path.iter() { args.robind(format!("/sys/{}", dir), format!("/sys/{}", dir)); } } fn module(&self) -> &'static str { - "SUSFS" + "sysfs" } } @@ -59,9 +62,5 @@ fn is_default_path(path: &Vec) -> bool { } fn default_path() -> Vec { - vec!("block".into(), - "bus".into(), - "class".into(), - "dev".into(), - "devices".into()) + vec!["block".into(), "bus".into(), "class".into(), "dev".into(), "devices".into()] } diff --git a/pacwrap-core/src/config/filesystem/to_home.rs b/pacwrap-core/src/config/filesystem/to_home.rs index b18129b..a3b025d 100644 --- a/pacwrap-core/src/config/filesystem/to_home.rs +++ b/pacwrap-core/src/config/filesystem/to_home.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -16,57 +16,48 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#![allow(non_camel_case_types)] - use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::InsVars, - config::filesystem::{Filesystem, - BindError, - default_permission, - is_default_permission}, - constants::HOME}; +use crate::{ + config::{ + filesystem::{default_permission, is_default_permission, BindError, Filesystem}, + InsVars, + }, + constants::HOME, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TO_HOME { - #[serde(skip_serializing_if = "is_default_permission", default = "default_permission")] - permission: String, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - path: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - filesystem: Vec +pub struct ToHome { + #[serde(skip_serializing_if = "Vec::is_empty", default, rename = "volumes")] + mounts: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] struct Mount { - #[serde(skip_serializing_if = "is_default_permission", default = "default_permission")] + #[serde(skip_serializing_if = "is_default_permission", default = "default_permission")] permission: String, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - path: Vec + #[serde(skip_serializing_if = "String::is_empty", default)] + path: String, + #[serde(skip_serializing_if = "String::is_empty", default)] + dest: String, } -#[typetag::serde] -impl Filesystem for TO_HOME { +#[typetag::serde(name = "to_home")] +impl Filesystem for ToHome { fn check(&self, _vars: &InsVars) -> Result<(), BindError> { - if self.path.len() > 0 { - if let Err(e) = check_mount(&self.permission, &self.path[0]) { - return Err(e); - } - } else { - if self.filesystem.len() == 0 { - Err(BindError::Warn(format!("Filesystem paths are undeclared.")))? - } + if self.mounts.len() == 0 { + Err(BindError::Warn(format!("Mount volumes undeclared.")))? } - for m in self.filesystem.iter() { + for m in self.mounts.iter() { if m.path.len() == 0 { - Err(BindError::Warn(format!("Filesystem paths are undeclared.")))? + Err(BindError::Warn(format!("Mount volumes undeclared.")))? } - if let Err(e) = check_mount(&m.permission, &m.path[0]) { + if let Err(e) = check_mount(&m.permission, &m.path) { return Err(e); } } @@ -75,45 +66,38 @@ impl Filesystem for TO_HOME { } fn register(&self, args: &mut ExecutionArgs, vars: &InsVars) { - if self.path.len() > 0 { - bind_filesystem(args,vars, &self.permission, &self.path); - } - - for m in self.filesystem.iter() { - bind_filesystem(args,vars, &m.permission, &m.path); + for m in self.mounts.iter() { + bind_filesystem(args, vars, &m.permission, &m.path, &m.dest); } } fn module(&self) -> &'static str { - "TO_HOME" + "to_home" } } -fn bind_filesystem(args: &mut ExecutionArgs, vars: &InsVars, permission: &str, path: &Vec) { - let src = &path[0]; - let mut dest: &String = src; - - if path.len() > 1 { - dest = &path[1]; - } - - let path_src = format!("{}/{}", *HOME, path[0]); - let path_dest = format!("{}/{}", vars.home_mount(), dest); +fn bind_filesystem(args: &mut ExecutionArgs, vars: &InsVars, permission: &str, src: &str, dest: &str) { + let dest = match dest.is_empty() { + false => dest, + true => src, + }; + let dest = format!("{}/{}", vars.home_mount(), dest); + let src = format!("{}/{}", *HOME, src); match permission == "rw" { - false => args.robind(path_src, path_dest), - true => args.bind(path_src, path_dest), + false => args.robind(src, dest), + true => args.bind(src, dest), } } fn check_mount(permission: &String, path: &String) -> Result<(), BindError> { - let per = permission.to_lowercase(); - + let per = permission.to_lowercase(); + if per != "ro" && per != "rw" { Err(BindError::Fail(format!("{} is an invalid permission.", permission)))? } - if ! Path::new(&format!("{}/{}", *HOME, &path)).exists() { + if !Path::new(&format!("{}/{}", *HOME, &path)).exists() { Err(BindError::Fail(format!("~/{} not found.", path)))? } diff --git a/pacwrap-core/src/config/filesystem/to_root.rs b/pacwrap-core/src/config/filesystem/to_root.rs index fcb27cb..f22a0c2 100644 --- a/pacwrap-core/src/config/filesystem/to_root.rs +++ b/pacwrap-core/src/config/filesystem/to_root.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -16,56 +16,47 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#![allow(non_camel_case_types)] - use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::InsVars, - config::filesystem::{Filesystem, - BindError, - default_permission, - is_default_permission}}; +use crate::{ + config::{ + filesystem::{default_permission, is_default_permission, BindError, Filesystem}, + InsVars, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TO_ROOT { - #[serde(skip_serializing_if = "is_default_permission", default = "default_permission")] - permission: String, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - path: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - filesystem: Vec +pub struct ToRoot { + #[serde(skip_serializing_if = "Vec::is_empty", default, rename = "volumes")] + mounts: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] struct Mount { - #[serde(skip_serializing_if = "is_default_permission", default = "default_permission")] + #[serde(skip_serializing_if = "is_default_permission", default = "default_permission")] permission: String, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - path: Vec + #[serde(skip_serializing_if = "String::is_empty", default)] + path: String, + #[serde(skip_serializing_if = "String::is_empty", default)] + dest: String, } -#[typetag::serde] -impl Filesystem for TO_ROOT { +#[typetag::serde(name = "to_root")] +impl Filesystem for ToRoot { fn check(&self, _vars: &InsVars) -> Result<(), BindError> { - if self.path.len() > 0 { - if let Err(e) = check_mount(&self.permission, &self.path[0]) { - return Err(e); - } - } else { - if self.filesystem.len() == 0 { - Err(BindError::Warn(format!("Filesystem paths are undeclared.")))? - } + if self.mounts.len() == 0 { + Err(BindError::Warn(format!("Mount volumes undeclared.")))? } - for m in self.filesystem.iter() { + for m in self.mounts.iter() { if m.path.len() == 0 { - Err(BindError::Warn(format!("Filesystem paths are undeclared.")))? + Err(BindError::Warn(format!("Mount volumes undeclared.")))? } - if let Err(e) = check_mount(&m.permission, &m.path[0]) { + if let Err(e) = check_mount(&m.permission, &m.path) { return Err(e); } } @@ -74,28 +65,22 @@ impl Filesystem for TO_ROOT { } fn register(&self, args: &mut ExecutionArgs, _vars: &InsVars) { - if self.path.len() > 0 { - bind_filesystem(args, &self.permission, &self.path); - } - - for m in self.filesystem.iter() { - bind_filesystem(args, &m.permission, &m.path); + for m in self.mounts.iter() { + bind_filesystem(args, &m.permission, &m.path, &m.dest); } } fn module(&self) -> &'static str { - "TO_HOME" + "to_root" } } -fn bind_filesystem(args: &mut ExecutionArgs, permission: &str, path: &Vec) { - let src = &path[0]; - let mut dest: &String = src; +fn bind_filesystem(args: &mut ExecutionArgs, permission: &str, src: &str, dest: &str) { + let dest = match dest.is_empty() { + true => src, + false => dest, + }; - if path.len() > 1 { - dest = &path[1]; - } - match permission == "rw" { false => args.robind(src, dest), true => args.bind(src, dest), @@ -103,15 +88,15 @@ fn bind_filesystem(args: &mut ExecutionArgs, permission: &str, path: &Vec Result<(), BindError> { - let per = permission.to_lowercase(); - + let per = permission.to_lowercase(); + if per != "ro" && per != "rw" { - Err(BindError::Fail(format!("{} is an invalid permission.", permission)))? + Err(BindError::Fail(format!("{} is an invalid permission.", permission)))? } - if ! Path::new(path).exists() { - Err(BindError::Fail(format!("Source path not found.")))? + if !Path::new(path).exists() { + Err(BindError::Fail(format!("Source path not found.")))? } - + Ok(()) } diff --git a/pacwrap-core/src/config/global.rs b/pacwrap-core/src/config/global.rs index b59c627..d218921 100644 --- a/pacwrap-core/src/config/global.rs +++ b/pacwrap-core/src/config/global.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -20,9 +20,13 @@ use std::process::exit; use lazy_static::lazy_static; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; -use crate::{Result, config::{config, save}, constants::CONFIG_FILE}; +use crate::{ + config::{config, save}, + constants::CONFIG_FILE, + Result, +}; lazy_static! { pub static ref CONFIG: Global = Global::load(); @@ -58,27 +62,27 @@ impl Default for ProgressKind { #[derive(Serialize, Deserialize, Clone)] pub struct Global { - #[serde(default = "Configuration::new")] + #[serde(default = "Configuration::new")] config: Configuration, - #[serde(default = "AlpmConfiguration::new")] + #[serde(default = "AlpmConfiguration::new")] alpm: AlpmConfiguration, } #[derive(Serialize, Deserialize, Clone)] pub struct Progress { - #[serde(default = "ProgressKind::default")] + #[serde(default = "ProgressKind::default")] transact: ProgressKind, - #[serde(default = "ProgressKind::default")] + #[serde(default = "ProgressKind::default")] download: ProgressKind, } #[derive(Serialize, Deserialize, Clone)] pub struct Configuration { - #[serde(default = "Verbosity::default")] + #[serde(default = "Verbosity::default")] summary: Verbosity, - #[serde(default = "Verbosity::default")] + #[serde(default = "Verbosity::default")] logging: Verbosity, - #[serde(default = "Progress::new")] + #[serde(default = "Progress::new")] progress: Progress, } @@ -88,15 +92,15 @@ pub struct AlpmConfiguration { ignore_pkg: Vec, #[serde(default = "hold_pkg")] hold_pkg: Vec, - #[serde(default = "sig_level")] + #[serde(default = "sig_level")] sig_level: String, - #[serde(default = "sig_level_opt")] + #[serde(default = "sig_level_opt")] sig_level_local: String, - #[serde(default = "parallel_downloads")] + #[serde(default = "parallel_downloads")] parallel_downloads: u32, - #[serde(default = "default_true")] + #[serde(default = "default_true")] check_space: bool, - #[serde(default = "default_true")] + #[serde(default = "default_true")] download_timeout: bool, } @@ -126,7 +130,7 @@ impl Progress { fn new() -> Self { Self { transact: ProgressKind::CondensedForeign, - download: ProgressKind::CondensedForeign, + download: ProgressKind::CondensedForeign, } } } @@ -136,8 +140,8 @@ impl AlpmConfiguration { Self { ignore_pkg: ignore_pkg(), hold_pkg: hold_pkg(), - sig_level: sig_level(), - sig_level_local: sig_level_opt(), + sig_level: sig_level(), + sig_level_local: sig_level_opt(), parallel_downloads: parallel_downloads(), check_space: true, download_timeout: true, @@ -149,7 +153,7 @@ impl AlpmConfiguration { } pub fn download_timeout(&self) -> bool { - ! self.download_timeout + !self.download_timeout } pub fn parallel_downloads(&self) -> u32 { @@ -210,7 +214,7 @@ fn sig_level() -> String { } fn sig_level_opt() -> String { - "Optional".into() + "Optional".into() } fn parallel_downloads() -> u32 { diff --git a/pacwrap-core/src/config/init.rs b/pacwrap-core/src/config/init.rs index fe9716b..02e77ba 100644 --- a/pacwrap-core/src/config/init.rs +++ b/pacwrap-core/src/config/init.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,14 +17,16 @@ * along with this program. If not, see . */ -use std::{path::Path, fs::File, io::Write}; +use std::{fs::File, io::Write, path::Path}; -use crate::{err, - Error, - Result, - ErrorKind, +use crate::{ + config::global::CONFIG, constants::{CACHE_DIR, CONFIG_DIR, DATA_DIR}, - config::global::CONFIG}; + err, + Error, + ErrorKind, + Result, +}; static REPO_CONF_DEFAULT: &'static str = r###"## See the pacman.conf(5) manpage for information on repository directives. ## All other libalpm-related options therein are ignored. @@ -72,7 +74,7 @@ impl DirectoryLayout { fn instantiate(self) -> Result<()> { for dir in self.dirs { let path: &str = &format!("{}{}", self.root, dir); - + if Path::new(path).exists() { continue; } @@ -88,22 +90,22 @@ impl DirectoryLayout { fn data_layout() -> DirectoryLayout { DirectoryLayout { - dirs: vec!("/root", "/home", "/state", "/pacman/sync"), + dirs: vec!["/root", "/home", "/state", "/pacman/sync"], root: *DATA_DIR, } } fn cache_layout() -> DirectoryLayout { DirectoryLayout { - dirs: vec!("/pkg"), - root: *CACHE_DIR + dirs: vec!["/pkg"], + root: *CACHE_DIR, } } fn config_layout() -> DirectoryLayout { DirectoryLayout { - dirs: vec!("/instance"), - root: *CONFIG_DIR + dirs: vec!["/instance"], + root: *CONFIG_DIR, } } @@ -114,9 +116,9 @@ fn write_to_file(location: &str, contents: &str) -> Result<()> { let mut f = match File::create(&location) { Ok(f) => f, - Err(error) => err!(ErrorKind::IOError(location.into(), error.kind()))? + Err(error) => err!(ErrorKind::IOError(location.into(), error.kind()))?, }; - + if let Err(error) = write!(f, "{contents}") { err!(ErrorKind::IOError(location.into(), error.kind()))? } @@ -125,11 +127,13 @@ fn write_to_file(location: &str, contents: &str) -> Result<()> { } pub fn init() -> Result<()> { - let _ = *CONFIG; - config_layout().instantiate()?; data_layout().instantiate()?; cache_layout().instantiate()?; write_to_file(&format!("{}/repositories.conf", *CONFIG_DIR), REPO_CONF_DEFAULT)?; - write_to_file(&format!("{}/pacwrap.yml", *CONFIG_DIR), PACWRAP_CONF_DEFAULT) + write_to_file(&format!("{}/pacwrap.yml", *CONFIG_DIR), PACWRAP_CONF_DEFAULT)?; + + let _ = *CONFIG; + + Ok(()) } diff --git a/pacwrap-core/src/config/instance.rs b/pacwrap-core/src/config/instance.rs index 9afc416..2e7ca15 100644 --- a/pacwrap-core/src/config/instance.rs +++ b/pacwrap-core/src/config/instance.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,33 +17,39 @@ * along with this program. If not, see . */ -use std::{borrow::Cow, - fmt::{Display, Debug, Formatter}, - vec::Vec}; +use std::{ + borrow::Cow, + fmt::{Debug, Display, Formatter}, + vec::Vec, +}; use serde::{Deserialize, Serialize}; -use crate::{Result, - config::{permission::{Permission, none::NONE}, - filesystem::{Filesystem, root::ROOT, home::HOME}, +use crate::{ + config::{ dbus::Dbus, - vars::InsVars, - save}, - constants::UNIX_TIMESTAMP}; + filesystem::{home::Home, root::Root, Filesystem}, + permission::{none::None, Permission}, + save, + vars::InsVars, + }, + constants::UNIX_TIMESTAMP, + Result, +}; #[derive(Serialize, Deserialize, Clone)] pub struct Instance<'a> { - #[serde(flatten)] + #[serde(flatten)] metadata: InstanceMetadata<'a>, #[serde(flatten)] runtime: InstanceRuntime, } -impl <'a>Instance<'a> { +impl<'a> Instance<'a> { pub fn new(ctype: InstanceType, deps: Vec<&'a str>, pkgs: Vec<&'a str>) -> Self { Self { - metadata: InstanceMetadata::new(ctype,deps,pkgs), - runtime: InstanceRuntime::new(), + metadata: InstanceMetadata::new(ctype, deps, pkgs), + runtime: InstanceRuntime::new(), } } } @@ -54,7 +60,7 @@ pub struct InstanceHandle<'a> { vars: InsVars<'a>, } -impl <'a>InstanceHandle<'a> { +impl<'a> InstanceHandle<'a> { pub fn new(ins: Instance<'a>, ins_vars: InsVars<'a>) -> Self { Self { instance: ins, @@ -87,7 +93,7 @@ impl <'a>InstanceHandle<'a> { } } -impl <'a>Debug for InstanceHandle<'a> { +impl<'a> Debug for InstanceHandle<'a> { fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { write!(fmter, "{:?}", self.vars())?; write!(fmter, "{:?}", self.config()) @@ -96,31 +102,31 @@ impl <'a>Debug for InstanceHandle<'a> { #[derive(Serialize, Deserialize, Clone)] pub struct InstanceRuntime { - #[serde(default)] - enable_userns: bool, - #[serde(default)] + #[serde(default)] + enable_userns: bool, + #[serde(default)] retain_session: bool, #[serde(default = "default_true")] seccomp: bool, - #[serde(default)] - allow_forking: bool, - #[serde(skip_serializing_if = "Vec::is_empty", default)] + #[serde(default)] + allow_forking: bool, + #[serde(skip_serializing_if = "Vec::is_empty", default)] filesystems: Vec>, #[serde(skip_serializing_if = "Vec::is_empty", default)] permissions: Vec>, #[serde(skip_serializing_if = "Vec::is_empty", default)] - dbus: Vec>, + dbus: Vec>, } impl InstanceRuntime { pub fn new() -> Self { - let default_fs: [Box; 2] = [Box::new(ROOT {}), Box::new(HOME {})]; - let default_per: [Box; 1] = [Box::new(NONE {})]; + let default_fs: [Box; 2] = [Box::new(Root {}), Box::new(Home {})]; + let default_per: [Box; 1] = [Box::new(None {})]; let fs: Vec> = Vec::from(default_fs); - let per: Vec> = Vec::from(default_per); - + let per: Vec> = Vec::from(default_per); + Self { - seccomp: true, + seccomp: true, allow_forking: false, retain_session: false, enable_userns: false, @@ -130,28 +136,28 @@ impl InstanceRuntime { } } - pub fn permissions(&self) -> &Vec> { - &self.permissions + pub fn permissions(&self) -> &Vec> { + &self.permissions } - - pub fn filesystem(&self) -> &Vec> { - &self.filesystems + + pub fn filesystem(&self) -> &Vec> { + &self.filesystems } - - pub fn dbus(&self) -> &Vec> { - &self.dbus - } - - pub fn allow_forking(&self) -> &bool { - &self.allow_forking + + pub fn dbus(&self) -> &Vec> { + &self.dbus } - - pub fn enable_userns(&self) -> &bool { - &self.enable_userns - } - - pub fn retain_session(&self) -> &bool { - &self.retain_session + + pub fn allow_forking(&self) -> &bool { + &self.allow_forking + } + + pub fn enable_userns(&self) -> &bool { + &self.enable_userns + } + + pub fn retain_session(&self) -> &bool { + &self.retain_session } pub fn seccomp(&self) -> &bool { @@ -170,30 +176,19 @@ impl Debug for InstanceRuntime { #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug)] pub enum InstanceType { - LINK, - BASE, - DEP, - ROOT + Symbolic, + Base, + Slice, + Aggregate, } -#[allow(dead_code)] impl InstanceType { - pub fn new(instype: &str) -> Self { - match instype { - "BASE" => Self::BASE, - "ROOT" => Self::ROOT, - "DEP" => Self::DEP, - "LINK" => Self::LINK, - _ => Self::BASE - } - } - fn as_str<'a>(&self) -> &'a str { match self { - Self::ROOT => "ROOT", - Self::LINK => "LINK", - Self::BASE => "BASE", - Self::DEP => "DEP" + Self::Symbolic => "LINK", + Self::Base => "BASE", + Self::Slice => "DEP", + Self::Aggregate => "ROOT", } } } @@ -206,23 +201,23 @@ impl Display for InstanceType { impl Default for InstanceType { fn default() -> Self { - Self::BASE + Self::Base } } #[derive(Serialize, Deserialize, Clone)] pub struct InstanceMetadata<'a> { - #[serde(default)] - container_type: InstanceType, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - dependencies: Vec>, - #[serde(skip_serializing_if = "Vec::is_empty", default)] + #[serde(default)] + container_type: InstanceType, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + dependencies: Vec>, + #[serde(skip_serializing_if = "Vec::is_empty", default)] explicit_packages: Vec>, #[serde(default = "time_as_seconds")] meta_version: u64, } -impl <'a>InstanceMetadata<'a> { +impl<'a> InstanceMetadata<'a> { fn new(ctype: InstanceType, deps: Vec<&'a str>, pkgs: Vec<&'a str>) -> Self { Self { container_type: ctype, @@ -238,15 +233,15 @@ impl <'a>InstanceMetadata<'a> { self.meta_version = *UNIX_TIMESTAMP; } - pub fn container_type(&self) -> &InstanceType { - &self.container_type + pub fn container_type(&self) -> &InstanceType { + &self.container_type } - pub fn dependencies(&'a self) -> Vec<&'a str> { + pub fn dependencies(&'a self) -> Vec<&'a str> { self.dependencies.iter().map(|a| a.as_ref()).collect() } - - pub fn explicit_packages(&'a self) -> Vec<&'a str> { + + pub fn explicit_packages(&'a self) -> Vec<&'a str> { self.explicit_packages.iter().map(|a| a.as_ref()).collect() } diff --git a/pacwrap-core/src/config/permission.rs b/pacwrap-core/src/config/permission.rs index 9f5f0d4..037ab33 100644 --- a/pacwrap-core/src/config/permission.rs +++ b/pacwrap-core/src/config/permission.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,21 +21,21 @@ use std::fmt::{Display, Formatter}; use crate::exec::args::ExecutionArgs; -use dyn_clone::{DynClone, clone_trait_object}; +use dyn_clone::{clone_trait_object, DynClone}; -pub mod none; +mod dev; mod display; -mod pulseaudio; -mod pipewire; mod env; mod gpu; mod net; -mod dev; +pub mod none; +mod pipewire; +mod pulseaudio; pub enum Condition { Success, SuccessWarn(String), - Nothing + Nothing, } #[derive(Debug, Clone)] @@ -44,7 +44,7 @@ pub enum PermError { Warn(String), } -#[typetag::serde(tag = "permission")] +#[typetag::serde(tag = "module")] pub trait Permission: DynClone { fn check(&self) -> Result, PermError>; fn register(&self, args: &mut ExecutionArgs); @@ -56,7 +56,6 @@ impl Display for PermError { match self { Self::Fail(error) => write!(fmter, "{}", error), Self::Warn(error) => write!(fmter, "{}", error), - } } } diff --git a/pacwrap-core/src/config/permission/dev.rs b/pacwrap-core/src/config/permission/dev.rs index 54fe3ac..5b2f7d7 100644 --- a/pacwrap-core/src/config/permission/dev.rs +++ b/pacwrap-core/src/config/permission/dev.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,28 +21,32 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::{Condition::Success, PermError::Fail}}; +use crate::{ + config::{ + permission::{Condition::Success, PermError::Fail, *}, + Permission, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct DEV { - devices: Vec +struct Dev { + devices: Vec, } -#[typetag::serde] -impl Permission for DEV { - fn check(&self) -> Result, PermError> { +#[typetag::serde(name = "dev")] +impl Permission for Dev { + fn check(&self) -> Result, PermError> { for device in self.devices.iter() { - if ! Path::new(&format!("/dev/{}", device)).exists() { + if !Path::new(&format!("/dev/{}", device)).exists() { Err(Fail(format!("/dev/{} is inaccessible.", device)))? } } Ok(Some(Success)) } - - fn register(&self, args: &mut ExecutionArgs) { + + fn register(&self, args: &mut ExecutionArgs) { for device in self.devices.iter() { args.dev(&format!("/dev/{}", device)); } diff --git a/pacwrap-core/src/config/permission/display.rs b/pacwrap-core/src/config/permission/display.rs index e69eb9e..4997941 100644 --- a/pacwrap-core/src/config/permission/display.rs +++ b/pacwrap-core/src/config/permission/display.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,34 +21,42 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::{ - Condition::{Success, SuccessWarn}, - PermError::Fail}, - constants::{XDG_RUNTIME_DIR, WAYLAND_SOCKET, WAYLAND_DISPLAY, X11_DISPLAY, XAUTHORITY}, - utils::check_socket}; +use crate::{ + config::{ + permission::{ + Condition::{Success, SuccessWarn}, + PermError::Fail, + *, + }, + Permission, + }, + constants::{WAYLAND_DISPLAY, WAYLAND_SOCKET, X11_DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR}, + exec::args::ExecutionArgs, + utils::check_socket, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct DISPLAY; +struct Display; -#[typetag::serde] -impl Permission for DISPLAY { +#[typetag::serde(name = "display")] +impl Permission for Display { fn check(&self) -> Result, PermError> { let mut bound = None; - match validate_wayland_socket() { - Ok(b) => if let Some(con) = b { - bound = Some(con); - }, - Err(e) => Err(e)? + match validate_wayland_socket() { + Ok(b) => + if let Some(con) = b { + bound = Some(con); + }, + Err(e) => Err(e)?, } - match validate_xorg_socket() { - Ok(b) => if let Some(con) = b { - bound = Some(con); - }, - Err(e) => Err(e)? + match validate_xorg_socket() { + Ok(b) => + if let Some(con) = b { + bound = Some(con); + }, + Err(e) => Err(e)?, } if let None = bound { @@ -56,30 +64,30 @@ impl Permission for DISPLAY { } else { Ok(bound) } - } - - fn register(&self, args: &mut ExecutionArgs) { - if ! WAYLAND_DISPLAY.is_empty() { - configure_wayland(args); - } + } - if ! X11_DISPLAY.is_empty() { - configure_xorg(args); - } + fn register(&self, args: &mut ExecutionArgs) { + if !WAYLAND_DISPLAY.is_empty() { + configure_wayland(args); + } + + if !X11_DISPLAY.is_empty() { + configure_xorg(args); + } } fn module(&self) -> &'static str { - "DISPLAY" + "display" } } fn validate_wayland_socket() -> Result, PermError> { - if ! WAYLAND_DISPLAY.is_empty() { - if ! Path::new(&*WAYLAND_SOCKET).exists() { + if !WAYLAND_DISPLAY.is_empty() { + if !Path::new(&*WAYLAND_SOCKET).exists() { Err(Fail(format!("Wayland socket '{}' not found.", &*WAYLAND_SOCKET)))? } - if ! check_socket(&*WAYLAND_SOCKET) { + if !check_socket(&*WAYLAND_SOCKET) { Err(Fail(format!("'{}' is not a valid UNIX socket.", &*WAYLAND_SOCKET)))? } @@ -89,30 +97,30 @@ fn validate_wayland_socket() -> Result, PermError> { Ok(None) } -fn validate_xorg_socket() -> Result, PermError> { - if ! X11_DISPLAY.is_empty() { +fn validate_xorg_socket() -> Result, PermError> { + if !X11_DISPLAY.is_empty() { let display: Vec<&str> = X11_DISPLAY.split(":").collect(); let xorg_socket = format!("/tmp/.X11-unix/X{}", display[1]); - + if XAUTHORITY.is_empty() { - Err(Fail(format!("XAUTHORITY environment variable unspecified.")))? + Err(Fail(format!("XAUTHORITY environment variable unspecified.")))? } - - if ! Path::new(*XAUTHORITY).exists() { + + if !Path::new(*XAUTHORITY).exists() { Err(Fail(format!("Xauthority file '{}' not found.", *XAUTHORITY)))? } - - if display[0].is_empty() || display[0] == "unix" { + + if display[0].is_empty() || display[0] == "unix" { if Path::new(&xorg_socket).exists() { - if ! check_socket(&xorg_socket) { + if !check_socket(&xorg_socket) { Err(Fail(format!("'{}' is not a valid UNIX socket.", &xorg_socket)))? } - + return Ok(Some(Success)); } else { Err(Fail(format!("X11 socket '{}' not found.", &xorg_socket)))? - } - } else { + } + } else { return Ok(Some(SuccessWarn(format!("Connecting to TCP X11 socket at '{}'", *X11_DISPLAY)))); } } @@ -121,7 +129,7 @@ fn validate_xorg_socket() -> Result, PermError> { } fn configure_wayland(args: &mut ExecutionArgs) { - let wayland_socket = format!("{}/{}", *XDG_RUNTIME_DIR, *WAYLAND_DISPLAY); + let wayland_socket = format!("{}/{}", *XDG_RUNTIME_DIR, *WAYLAND_DISPLAY); args.env("WAYLAND_DISPLAY", *WAYLAND_DISPLAY); args.robind(&wayland_socket, &wayland_socket); @@ -130,13 +138,13 @@ fn configure_wayland(args: &mut ExecutionArgs) { fn configure_xorg(args: &mut ExecutionArgs) { let display: Vec<&str> = X11_DISPLAY.split(":").collect(); let xorg_socket = format!("/tmp/.X11-unix/X{}", display[1]); - let container_xauth = format!("{}/Xauthority", *XDG_RUNTIME_DIR); - + let container_xauth = format!("{}/Xauthority", *XDG_RUNTIME_DIR); + args.env("DISPLAY", *X11_DISPLAY); - args.env("XAUTHORITY", &container_xauth); + args.env("XAUTHORITY", &container_xauth); args.robind(*XAUTHORITY, &container_xauth); - if display[0].is_empty() || display[0] == "unix" { + if display[0].is_empty() || display[0] == "unix" { args.robind(&xorg_socket, &xorg_socket); } } diff --git a/pacwrap-core/src/config/permission/env.rs b/pacwrap-core/src/config/permission/env.rs index 76a0290..4693187 100644 --- a/pacwrap-core/src/config/permission/env.rs +++ b/pacwrap-core/src/config/permission/env.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,39 +21,42 @@ use std::env; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, +use crate::{ + config::{ + permission::{Condition::Success, *}, + Permission, + }, + exec::args::ExecutionArgs, utils::print_warning, - config::{Permission, permission::*}, - config::permission::Condition::Success}; +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ENV { - #[serde(skip_serializing_if = "String::is_empty", default)] +pub struct Environment { + #[serde(skip_serializing_if = "String::is_empty", default)] var: String, - #[serde(skip_serializing_if = "String::is_empty", default)] + #[serde(skip_serializing_if = "String::is_empty", default)] set: String, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - variables: Vec + #[serde(skip_serializing_if = "Vec::is_empty", default)] + variables: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] struct Var { var: String, - #[serde(skip_serializing_if = "String::is_empty", default)] - set: String + #[serde(skip_serializing_if = "String::is_empty", default)] + set: String, } -#[typetag::serde] -impl Permission for ENV { - +#[typetag::serde(name = "env")] +impl Permission for Environment { fn check(&self) -> Result, PermError> { Ok(Some(Success)) } - fn register(&self, args: &mut ExecutionArgs) { + fn register(&self, args: &mut ExecutionArgs) { if self.var != "" { let set = env_var(&self.var, &self.set); - args.env(&self.var, set); + args.env(&self.var, set); } for v in self.variables.iter() { @@ -63,20 +66,20 @@ impl Permission for ENV { } fn module(&self) -> &'static str { - "ENV" + "env" } } fn env_var(var: &String, set: &String) -> String { - if set != "" { - return set.to_owned(); + if set != "" { + return set.to_owned(); } - - match env::var(&var) { - Ok(env) => env, + + match env::var(&var) { + Ok(env) => env, Err(_) => { print_warning(format!("Environment variable {} is unset.", var)); "".into() } - } + } } diff --git a/pacwrap-core/src/config/permission/gpu.rs b/pacwrap-core/src/config/permission/gpu.rs index 5565101..a46dae2 100644 --- a/pacwrap-core/src/config/permission/gpu.rs +++ b/pacwrap-core/src/config/permission/gpu.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,45 +17,48 @@ * along with this program. If not, see . */ -use std::fs::read_dir; -use std::path::Path; +use std::{fs::read_dir, path::Path}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::{Condition::Success, PermError::Fail}}; +use crate::{ + config::{ + permission::{Condition::Success, PermError::Fail, *}, + Permission, + }, + exec::args::ExecutionArgs, +}; lazy_static! { static ref GPU_DEV: Vec = populate_dev(); } #[derive(Debug, Clone, Serialize, Deserialize)] -struct GPU; +struct Graphics; -#[typetag::serde] -impl Permission for GPU { - fn check(&self) -> Result, PermError> { - if ! Path::new("/dev").exists() { +#[typetag::serde(name = "gpu")] +impl Permission for Graphics { + fn check(&self) -> Result, PermError> { + if !Path::new("/dev").exists() { Err(Fail(format!("/dev is inaccessible.")))? } if GPU_DEV.len() == 0 { - Err(Fail(format!("No graphics devices are available.")))? + Err(Fail(format!("No graphics devices are available.")))? } Ok(Some(Success)) } - - fn register(&self, args: &mut ExecutionArgs) { + + fn register(&self, args: &mut ExecutionArgs) { for dev in GPU_DEV.iter() { args.dev(dev); } } fn module(&self) -> &'static str { - "GPU" + "gpu" } } @@ -67,7 +70,7 @@ fn populate_dev() -> Vec { let file = f.file_name(); let dev = file.to_str().unwrap(); if dev.starts_with("nvidia") || dev == "dri" { - vec.push(format!("/dev/{}",dev)); + vec.push(format!("/dev/{}", dev)); } } } diff --git a/pacwrap-core/src/config/permission/net.rs b/pacwrap-core/src/config/permission/net.rs index 4672df9..ca9ca9b 100644 --- a/pacwrap-core/src/config/permission/net.rs +++ b/pacwrap-core/src/config/permission/net.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,15 +19,19 @@ use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::Condition::Success}; +use crate::{ + config::{ + permission::{Condition::Success, *}, + Permission, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NET; +pub struct Network; -#[typetag::serde] -impl Permission for NET { +#[typetag::serde(name = "net")] +impl Permission for Network { fn check(&self) -> Result, PermError> { Ok(Some(Success)) } @@ -38,6 +42,6 @@ impl Permission for NET { } fn module(&self) -> &'static str { - "NET" + "net" } } diff --git a/pacwrap-core/src/config/permission/none.rs b/pacwrap-core/src/config/permission/none.rs index dc9bc33..db79947 100644 --- a/pacwrap-core/src/config/permission/none.rs +++ b/pacwrap-core/src/config/permission/none.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,22 +19,26 @@ use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::Condition::Success}; +use crate::{ + config::{ + permission::{Condition::Success, *}, + Permission, + }, + exec::args::ExecutionArgs, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NONE; +pub struct None; -#[typetag::serde] -impl Permission for NONE { - fn check(&self) -> Result, PermError> { - Ok(Some(Success)) +#[typetag::serde(name = "none")] +impl Permission for None { + fn check(&self) -> Result, PermError> { + Ok(Some(Success)) } fn register(&self, _: &mut ExecutionArgs) {} - fn module(&self) -> &'static str { - "NONE" + fn module(&self) -> &'static str { + "none" } } diff --git a/pacwrap-core/src/config/permission/pipewire.rs b/pacwrap-core/src/config/permission/pipewire.rs index 1d44c25..5c2f663 100644 --- a/pacwrap-core/src/config/permission/pipewire.rs +++ b/pacwrap-core/src/config/permission/pipewire.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,38 +21,42 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::{Condition::Success, PermError::Warn}, +use crate::{ + config::{ + permission::{Condition::Success, PermError::Warn, *}, + Permission, + }, constants::XDG_RUNTIME_DIR, - utils::check_socket}; + exec::args::ExecutionArgs, + utils::check_socket, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct PIPEWIRE { +struct Pipewire { #[serde(skip_serializing_if = "is_default_socket", default = "default_socket")] socket: String, } -#[typetag::serde] -impl Permission for PIPEWIRE { - fn check(&self) -> Result, PermError> { - if ! Path::new(&self.socket).exists() { +#[typetag::serde(name = "pipewire")] +impl Permission for Pipewire { + fn check(&self) -> Result, PermError> { + if !Path::new(&self.socket).exists() { Err(Warn(format!("Pipewire socket not found.")))? } - if ! check_socket(&self.socket) { + if !check_socket(&self.socket) { Err(Warn(format!("'{}' is not a valid UNIX socket.", &self.socket)))? } Ok(Some(Success)) } - - fn register(&self, args: &mut ExecutionArgs) { + + fn register(&self, args: &mut ExecutionArgs) { args.robind(&self.socket, default_socket()); } fn module(&self) -> &'static str { - "PIPEWIRE" + "pipewire" } } diff --git a/pacwrap-core/src/config/permission/pulseaudio.rs b/pacwrap-core/src/config/permission/pulseaudio.rs index df7cb2f..33aed85 100644 --- a/pacwrap-core/src/config/permission/pulseaudio.rs +++ b/pacwrap-core/src/config/permission/pulseaudio.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,38 +21,42 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::{exec::args::ExecutionArgs, - config::{Permission, permission::*}, - config::permission::{Condition::Success, PermError::Warn}, +use crate::{ + config::{ + permission::{Condition::Success, PermError::Warn, *}, + Permission, + }, constants::XDG_RUNTIME_DIR, - utils::check_socket}; + exec::args::ExecutionArgs, + utils::check_socket, +}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct PULSEAUDIO { +struct Pulseaudio { #[serde(skip_serializing_if = "is_default_socket", default = "default_socket")] socket: String, } -#[typetag::serde] -impl Permission for PULSEAUDIO { - fn check(&self) -> Result, PermError> { - if ! Path::new(&self.socket).exists() { +#[typetag::serde(name = "pulseaudio")] +impl Permission for Pulseaudio { + fn check(&self) -> Result, PermError> { + if !Path::new(&self.socket).exists() { Err(Warn(format!("Pulseaudio socket not found.")))? } - if ! check_socket(&self.socket) { + if !check_socket(&self.socket) { Err(Warn(format!("'{}' is not a valid UNIX socket.", &self.socket)))? } Ok(Some(Success)) } - - fn register(&self, args: &mut ExecutionArgs) { + + fn register(&self, args: &mut ExecutionArgs) { args.robind(&self.socket, default_socket()); } fn module(&self) -> &'static str { - "PULSEAUDIO" + "pulseaudio" } } diff --git a/pacwrap-core/src/config/register.rs b/pacwrap-core/src/config/register.rs index 85ff63c..f0985a9 100644 --- a/pacwrap-core/src/config/register.rs +++ b/pacwrap-core/src/config/register.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,26 +19,30 @@ use std::vec::Vec; -use crate::{exec::args::ExecutionArgs, - config::{InsVars, - Permission, - Dbus, +use crate::{ + config::{ + filesystem::{BindError, Filesystem}, permission::*, - filesystem::{Filesystem, BindError}}, - utils::print_warning, + Dbus, + InsVars, + Permission, + }, + err, error::*, - err}; + exec::args::ExecutionArgs, + utils::print_warning, +}; use super::ConfigError; pub fn register_filesystems(per: &Vec>, vars: &InsVars, args: &mut ExecutionArgs) -> Result<()> { for p in per.iter() { - match p.check(vars) { + match p.check(vars) { Ok(_) => p.register(args, vars), Err(condition) => match condition { BindError::Warn(_) => print_warning(ConfigError::Filesystem(p.module(), condition)), - BindError::Fail(_) => err!(ConfigError::Filesystem(p.module(), condition))? - } + BindError::Fail(_) => err!(ConfigError::Filesystem(p.module(), condition))?, + }, } } @@ -51,18 +55,18 @@ pub fn register_permissions(per: &Vec>, args: &mut Execution Ok(condition) => match condition { Some(b) => { p.register(args); - + if let Condition::SuccessWarn(warning) = b { print_warning(format!("{}: {} ", p.module(), warning)); } - }, - None => continue + } + None => continue, }, Err(condition) => match condition { PermError::Warn(_) => print_warning(ConfigError::Permission(p.module(), condition)), - PermError::Fail(_) => err!(ConfigError::Permission(p.module(), condition))? - } - } + PermError::Fail(_) => err!(ConfigError::Permission(p.module(), condition))?, + }, + } } Ok(()) diff --git a/pacwrap-core/src/config/vars.rs b/pacwrap-core/src/config/vars.rs index 2af3cf0..664a3da 100644 --- a/pacwrap-core/src/config/vars.rs +++ b/pacwrap-core/src/config/vars.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,9 +17,11 @@ * along with this program. If not, see . */ -use std::borrow::Cow; -use std::env::var; -use std::fmt::{Debug, Formatter}; +use std::{ + borrow::Cow, + env::var, + fmt::{Debug, Formatter}, +}; use crate::constants::{CACHE_DIR, CONFIG_DIR, DATA_DIR}; @@ -35,61 +37,63 @@ pub struct InsVars<'a> { pacman_gnupg: Cow<'a, str>, } -impl <'a>InsVars<'a> { +impl<'a> InsVars<'a> { pub fn new(ins: &'a str) -> Self { Self { - home: match var("PACWRAP_HOME") { - Err(_) => format!("{}/home/{ins}", *DATA_DIR), Ok(var) => var - }.into(), + home: match var("PACWRAP_HOME") { + Err(_) => format!("{}/home/{ins}", *DATA_DIR), + Ok(var) => var, + } + .into(), root: format!("{}/root/{ins}", *DATA_DIR).into(), pacman_gnupg: format!("{}/pacman/gnupg", *DATA_DIR).into(), pacman_cache: format!("{}/pkg", *CACHE_DIR).into(), - config: format!("{}/instance/{ins}.yml", *CONFIG_DIR).into(), - home_mount: format!("/home/{ins}").into(), + config: format!("{}/instance/{ins}.yml", *CONFIG_DIR).into(), + home_mount: format!("/home/{ins}").into(), user: ins.into(), instance: ins.into(), } } - pub fn pacman_cache(&self) -> &str { - &self.pacman_cache + pub fn pacman_cache(&self) -> &str { + &self.pacman_cache } - pub fn pacman_gnupg(&self) -> &str { - &self.pacman_gnupg + pub fn pacman_gnupg(&self) -> &str { + &self.pacman_gnupg } - pub fn config_path(&self) -> &str { - &self.config + pub fn config_path(&self) -> &str { + &self.config } - pub fn root(&self) -> &str { - &self.root + pub fn root(&self) -> &str { + &self.root } - pub fn home(&self) -> &str { - &self.home + pub fn home(&self) -> &str { + &self.home } - pub fn home_mount(&self) -> &str { - &self.home_mount + pub fn home_mount(&self) -> &str { + &self.home_mount } - pub fn user(&self) -> &str { - &self.user + pub fn user(&self) -> &str { + &self.user } - pub fn instance(&self) -> &str { - &self.instance + pub fn instance(&self) -> &str { + &self.instance } } -impl <'a>Debug for InsVars<'a> { +impl<'a> Debug for InsVars<'a> { fn fmt(&self, fmter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { writeln!(fmter, "Instance: {}", self.instance)?; writeln!(fmter, "Instance User: {}", self.user)?; - writeln!(fmter, "Instance Config: {}", self.config)?; - writeln!(fmter, "Instance Root: {}", self.root)?; + writeln!(fmter, "Instance Config: {}", self.config)?; + writeln!(fmter, "Instance Root: {}", self.root)?; writeln!(fmter, "Instance Home: {} -> {}", self.home, self.home_mount) } } diff --git a/pacwrap-core/src/constants.rs b/pacwrap-core/src/constants.rs index cefc8b7..3dc9917 100644 --- a/pacwrap-core/src/constants.rs +++ b/pacwrap-core/src/constants.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -20,14 +20,21 @@ use std::{env::var, process::id}; use lazy_static::lazy_static; -use nix::unistd::{geteuid, getegid}; +use nix::unistd::{getegid, geteuid}; -use crate::{error, Error, ErrorKind, utils::{is_color_terminal, is_truecolor_terminal, unix_time_as_seconds}}; +use crate::{ + error, + utils::{is_color_terminal, is_truecolor_terminal, unix_time_as_seconds}, + Error, + ErrorKind, +}; pub const BWRAP_EXECUTABLE: &str = "bwrap"; pub const DBUS_PROXY_EXECUTABLE: &str = "xdg-dbus-proxy"; pub const DEFAULT_PATH: &str = "/usr/local/bin:/usr/bin/:/bin"; -pub const PACMAN_KEY_SCRIPT: &'static str = "pacman-key"; +pub const PACMAN_KEY_SCRIPT: &str = "pacman-key"; +pub const RUNTIME_DIRECTORY: &str = "/usr/share/pacwrap/runtime"; +pub const RUNTIME_TLS_STORE: &str = "/etc/ca-certificates/extracted/tls-ca-bundle.pem"; const PACWRAP_CONFIG_DIR: &str = "/.config/pacwrap"; const PACWRAP_DATA_DIR: &str = "/.local/share/pacwrap"; @@ -43,26 +50,26 @@ macro_rules! format_str { #[macro_export] macro_rules! to_static_str { ( $x:expr ) => { - $x.to_string().leak() + $x.to_string().leak() }; } lazy_static! { pub static ref UID: u32 = geteuid().as_raw(); pub static ref GID: u32 = getegid().as_raw(); - pub static ref PWD: &'static str = env_opt("PWD"); + pub static ref PWD: &'static str = env_opt("PWD"); pub static ref HOME: &'static str = env("HOME"); pub static ref USER: &'static str = env("USER"); pub static ref TERM: &'static str = env_opt("TERM"); pub static ref COLORTERM: &'static str = env_opt("COLORTERM"); pub static ref LANG: &'static str = env_default("LAMG", "en_US.UTF-8"); pub static ref WAYLAND_DISPLAY: &'static str = env_opt("WAYLAND_DISPLAY"); - pub static ref X11_DISPLAY: &'static str = env_opt("DISPLAY"); + pub static ref X11_DISPLAY: &'static str = env_opt("DISPLAY"); pub static ref XAUTHORITY: &'static str = env_opt("XAUTHORITY"); pub static ref CACHE_DIR: &'static str = env_default_dir("PACWRAP_CACHE_DIR", PACWRAP_CACHE_DIR); pub static ref CONFIG_DIR: &'static str = env_default_dir("PACWRAP_CONFIG_DIR", PACWRAP_CONFIG_DIR); pub static ref DATA_DIR: &'static str = env_default_dir("PACWRAP_DATA_DIR", PACWRAP_DATA_DIR); - pub static ref CONFIG_FILE: &'static str = format_str!("{}/pacwrap.yml", *CONFIG_DIR); + pub static ref CONFIG_FILE: &'static str = format_str!("{}/pacwrap.yml", *CONFIG_DIR); pub static ref XDG_RUNTIME_DIR: String = format!("/run/user/{}", *UID); pub static ref DBUS_SOCKET: String = format!("/run/user/{}/pacwrap_dbus_{}", *UID, &id()); pub static ref WAYLAND_SOCKET: String = format!("{}{}", *XDG_RUNTIME_DIR, *WAYLAND_DISPLAY); @@ -76,82 +83,137 @@ lazy_static! { pub static ref BOLD_WHITE: &'static str = bold_white(); pub static ref BOLD_YELLOW: &'static str = bold_yellow(); pub static ref BOLD_RED: &'static str = bold_red(); - pub static ref BOLD_GREEN: &'static str = bold_green(); - pub static ref BAR_GREEN: &'static str = bar_green(); - pub static ref BAR_CYAN: &'static str = bar_cyan(); - pub static ref ARROW_CYAN: &'static str = arrow_cyan(); - pub static ref ARROW_RED: &'static str = arrow_red(); - pub static ref ARROW_GREEN: &'static str = arrow_green(); + pub static ref BOLD_GREEN: &'static str = bold_green(); + pub static ref BAR_GREEN: &'static str = bar_green(); + pub static ref BAR_CYAN: &'static str = bar_cyan(); + pub static ref ARROW_CYAN: &'static str = arrow_cyan(); + pub static ref ARROW_RED: &'static str = arrow_red(); + pub static ref ARROW_GREEN: &'static str = arrow_green(); } fn arrow_red() -> &'static str { - if *IS_COLOR_TERMINLAL { "->" } else { "->" } + if *IS_COLOR_TERMINLAL { + "->" + } else { + "->" + } } fn arrow_cyan() -> &'static str { - if *IS_COLOR_TERMINLAL { "->" } else { "->" } + if *IS_COLOR_TERMINLAL { + "->" + } else { + "->" + } } fn arrow_green() -> &'static str { - if *IS_COLOR_TERMINLAL { "->" } else { "->" } + if *IS_COLOR_TERMINLAL { + "->" + } else { + "->" + } } fn bar_green() -> &'static str { - if *IS_COLOR_TERMINLAL { "::" } else { "::" } + if *IS_COLOR_TERMINLAL { + "::" + } else { + "::" + } } fn bar_cyan() -> &'static str { - if *IS_COLOR_TERMINLAL { "::" } else { "::" } + if *IS_COLOR_TERMINLAL { + "::" + } else { + "::" + } } fn dim() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn bold() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn bold_white() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn bold_red() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn bold_yellow() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn bold_green() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn reset() -> &'static str { - if *IS_COLOR_TERMINLAL { "" } else { "" } + if *IS_COLOR_TERMINLAL { + "" + } else { + "" + } } fn env(env: &'static str) -> &'static str { match var(env) { - Ok(var) => var.leak(), Err(_) => { error!(ErrorKind::EnvVarUnset(env)).handle(); "" } + Ok(var) => var.leak(), + Err(_) => { + error!(ErrorKind::EnvVarUnset(env)).handle(); + "" + } } } fn env_opt(env: &str) -> &'static str { match var(env) { - Ok(var) => var.leak(), Err(_) => "", + Ok(var) => var.leak(), + Err(_) => "", } } fn env_default(env: &str, default: &'static str) -> &'static str { match var(env) { - Ok(var) => var.leak(), Err(_) => default, + Ok(var) => var.leak(), + Err(_) => default, } } fn env_default_dir(env: &str, default: &str) -> &'static str { match var(env) { - Ok(var) => var.leak(), Err(_) => format_str!("{}{}", *HOME, default), + Ok(var) => var.leak(), + Err(_) => format_str!("{}{}", *HOME, default), } } diff --git a/pacwrap-core/src/error.rs b/pacwrap-core/src/error.rs index f2f2b25..2801e18 100644 --- a/pacwrap-core/src/error.rs +++ b/pacwrap-core/src/error.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,9 +17,16 @@ * along with this program. If not, see . */ -use std::{any::Any, process::exit, fmt::{Display, Debug}}; +use std::{ + any::Any, + fmt::{Debug, Display}, + process::exit, +}; -use crate::{utils::{print_error, print_warning}, sync::SyncError}; +use crate::{ + sync::SyncError, + utils::{print_error, print_warning}, +}; pub type Result = std::result::Result; @@ -39,11 +46,11 @@ macro_rules! error { #[macro_export] macro_rules! impl_error { - ( $x:ident ) => { + ( $x:ident ) => { impl ErrorTrait for $x { fn code(&self) -> i32 { 1 - } + } } }; } @@ -63,9 +70,7 @@ pub struct Error { impl Error { pub fn new(err: Box) -> Self { - Self { - kind: err, - } + Self { kind: err } } pub fn handle(&self) { @@ -73,8 +78,8 @@ impl Error { match self.downcast::() { Ok(error) => match error { SyncError::TransactionFailureAgent => (), - _ => print_error(&self.kind), - } + _ => print_error(&self.kind), + }, Err(_) => print_error(&self.kind), } @@ -82,7 +87,7 @@ impl Error { } pub fn error(&self) -> i32 { - print_error(&self.kind); + print_error(&self.kind); self.kind.code() } @@ -92,12 +97,16 @@ impl Error { pub fn downcast(&self) -> std::result::Result<&T, &Self> { match self.kind.as_any().downcast_ref::() { - Some(inner) => Ok(inner), None => Err(self), + Some(inner) => Ok(inner), + None => Err(self), } } } -impl Downcast for T where T: ErrorTrait + 'static { +impl Downcast for T +where + T: ErrorTrait + 'static, +{ fn as_any(&self) -> &dyn Any { self } diff --git a/pacwrap-core/src/exec.rs b/pacwrap-core/src/exec.rs index e4596f5..609483a 100644 --- a/pacwrap-core/src/exec.rs +++ b/pacwrap-core/src/exec.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,31 +17,52 @@ * along with this program. If not, see . */ -use std::{process::{Child, Command, Stdio}, fmt::{Formatter, Display}}; +use std::{ + fmt::{Display, Formatter}, + process::{Child, Command, Stdio}, +}; use command_fds::{CommandFdExt, FdMapping}; use lazy_static::lazy_static; -use crate::{err, - impl_error, - to_static_str, - ErrorTrait, - ErrorKind, - Error, - Result, - constants::{LOG_LOCATION, BWRAP_EXECUTABLE, BOLD, RESET, GID, UID, TERM, LANG, COLORTERM, PACMAN_KEY_SCRIPT}, +use crate::{ config::InstanceHandle, - sync::transaction::{TransactionParameters, TransactionMetadata}, - exec::{utils::{wait_on_process, agent_params}, seccomp::{provide_bpf_program, FilterType::*}}}; + constants::{ + BOLD, + BWRAP_EXECUTABLE, + COLORTERM, + GID, + LANG, + LOG_LOCATION, + PACMAN_KEY_SCRIPT, + RESET, + RUNTIME_DIRECTORY, + RUNTIME_TLS_STORE, + TERM, + UID, + }, + err, + exec::{ + seccomp::{provide_bpf_program, FilterType::*}, + utils::{agent_params, wait_on_process}, + }, + impl_error, + sync::transaction::{TransactionMetadata, TransactionParameters}, + to_static_str, + Error, + ErrorKind, + ErrorTrait, + Result, +}; pub mod args; -pub mod utils; pub mod seccomp; +pub mod utils; lazy_static! { static ref ID: (&'static str, &'static str) = (to_static_str!(UID), to_static_str!(GID)); - static ref DIST_IMG: &'static str = option_env!("PACWRAP_DIST_IMG").unwrap_or("/usr/share/pacwrap/runtime"); - static ref DIST_TLS: &'static str = option_env!("PACWRAP_DIST_TLS").unwrap_or("/etc/ca-certificates/extracted/tls-ca-bundle.pem"); + static ref DIST_IMG: &'static str = option_env!("PACWRAP_DIST_IMG").unwrap_or(RUNTIME_DIRECTORY); + static ref DIST_TLS: &'static str = option_env!("PACWRAP_DIST_TLS").unwrap_or(RUNTIME_TLS_STORE); } #[derive(Debug, Clone)] @@ -59,126 +80,136 @@ impl_error!(ExecutionError); impl Display for ExecutionError { fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { + match self { Self::InvalidPathVar(dir, err) => write!(fmter, "Invalid {}PATH{} variable '{dir}': {err}", *BOLD, *RESET), Self::ExecutableUnavailable(exec) => write!(fmter, "'{}': Not available in container {}PATH{}.", exec, *BOLD, *RESET), Self::UnabsolutePath(path) => write!(fmter, "'{}': {}PATH{} variable must be absolute", path, *BOLD, *RESET), - Self::UnabsoluteExec(path) => write!(fmter, "'{}': Executable path must be absolute.", path), + Self::UnabsoluteExec(path) => write!(fmter, "'{}': Executable path must be absolute.", path), Self::DirectoryNotExecutable(path) => write!(fmter, "'{}': Directories are not executables.", path), Self::SocketTimeout(socket) => write!(fmter, "Socket '{socket}': timed out."), - Self::RuntimeArguments => write!(fmter, "Invalid runtime arguments."), + Self::RuntimeArguments => write!(fmter, "Invalid runtime arguments."), } } } +#[rustfmt::skip] pub fn fakeroot_container(ins: &InstanceHandle, arguments: Vec<&str>) -> Result { - let (sec_reader, sec_writer) = os_pipe::pipe().unwrap(); + let (sec_reader, sec_writer) = os_pipe::pipe().unwrap(); let sec_fd = provide_bpf_program(vec![Standard, Namespaces], &sec_reader, sec_writer).unwrap(); - let fd_mappings = vec![FdMapping { parent_fd: sec_fd, child_fd: sec_fd }]; + let fd_mappings = vec![FdMapping { + parent_fd: sec_fd, + child_fd: sec_fd }]; - match Command::new(BWRAP_EXECUTABLE) - .env_clear() - .arg("--tmpfs").arg("/tmp") + match Command::new(BWRAP_EXECUTABLE).env_clear() + .arg("--tmpfs").arg("/tmp") .arg("--bind").arg(ins.vars().root()).arg("/") - .arg("--ro-bind").arg(format!("{}/lib", *DIST_IMG)).arg("/tmp/runtime") - .arg("--ro-bind").arg("/etc/resolv.conf").arg("/etc/resolv.conf") - .arg("--ro-bind").arg("/etc/localtime").arg("/etc/localtime") - .arg("--bind").arg(ins.vars().pacman_gnupg()).arg("/etc/pacman.d/gnupg") - .arg("--bind").arg(ins.vars().pacman_cache()).arg("/var/cache/pacman/pkg") - .arg("--bind").arg(ins.vars().home()).arg(ins.vars().home_mount()) - .arg("--dev").arg("/dev") - .arg("--proc").arg("/proc") - .arg("--unshare-all").arg("--share-net") - .arg("--hostname").arg("FakeChroot") - .arg("--new-session") - .arg("--setenv").arg("TERM").arg("xterm") - .arg("--setenv").arg("PATH").arg("/usr/local/bin:/usr/bin") - .arg("--setenv").arg("CWD").arg(ins.vars().home_mount()) - .arg("--setenv").arg("HOME").arg(ins.vars().home_mount()) - .arg("--setenv").arg("USER").arg(ins.vars().user()) - .arg("--die-with-parent") - .arg("--disable-userns") - .arg("--unshare-user") - .arg("--seccomp") - .arg(sec_fd.to_string()) - .arg("fakechroot") - .arg("fakeroot") - .args(arguments) - .fd_mappings(fd_mappings) - .unwrap() - .spawn() { - Ok(child) => Ok(child), - Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())), + .arg("--ro-bind").arg(format!("{}/lib", *DIST_IMG)).arg("/tmp/runtime") + .arg("--ro-bind").arg("/etc/resolv.conf").arg("/etc/resolv.conf") + .arg("--ro-bind").arg("/etc/localtime").arg("/etc/localtime") + .arg("--bind").arg(ins.vars().pacman_gnupg()).arg("/etc/pacman.d/gnupg") + .arg("--bind").arg(ins.vars().pacman_cache()).arg("/var/cache/pacman/pkg") + .arg("--bind").arg(ins.vars().home()).arg(ins.vars().home_mount()) + .arg("--dev").arg("/dev") + .arg("--proc").arg("/proc") + .arg("--unshare-all").arg("--share-net") + .arg("--hostname").arg("FakeChroot") + .arg("--new-session") + .arg("--setenv").arg("TERM").arg("xterm") + .arg("--setenv").arg("PATH").arg("/usr/local/bin:/usr/bin") + .arg("--setenv").arg("CWD").arg(ins.vars().home_mount()) + .arg("--setenv").arg("HOME").arg(ins.vars().home_mount()) + .arg("--setenv").arg("USER").arg(ins.vars().user()) + .arg("--die-with-parent") + .arg("--disable-userns") + .arg("--unshare-user") + .arg("--seccomp") + .arg(sec_fd.to_string()) + .arg("fakechroot") + .arg("fakeroot") + .args(arguments) + .fd_mappings(fd_mappings) + .unwrap() + .spawn() + { + Ok(child) => Ok(child), + Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())), } } +#[rustfmt::skip] pub fn transaction_agent(ins: &InstanceHandle, params: &TransactionParameters, metadata: &TransactionMetadata) -> Result { - let (sec_reader, sec_writer) = os_pipe::pipe().unwrap(); + let (sec_reader, sec_writer) = os_pipe::pipe().unwrap(); let (params_reader, params_writer) = os_pipe::pipe().unwrap(); let params_fd = agent_params(¶ms_reader, ¶ms_writer, params, metadata)?; let sec_fd = provide_bpf_program(vec![Standard, Namespaces], &sec_reader, sec_writer).unwrap(); let fd_mappings = vec![ - FdMapping { parent_fd: sec_fd, child_fd: sec_fd }, - FdMapping { parent_fd: params_fd, child_fd: params_fd }, + FdMapping { + parent_fd: sec_fd, + child_fd: sec_fd + }, + FdMapping { + parent_fd: params_fd, + child_fd: params_fd + }, ]; - match Command::new(BWRAP_EXECUTABLE) - .env_clear() - .arg("--bind").arg(&ins.vars().root()).arg("/mnt/fs") - .arg("--symlink").arg("/mnt/fs/usr").arg("/usr") - .arg("--ro-bind").arg(format!("{}/lib", *DIST_IMG)).arg("/lib64") - .arg("--ro-bind").arg(format!("{}/bin", *DIST_IMG)).arg("/bin") - .arg("--ro-bind").arg("/etc/resolv.conf").arg("/etc/resolv.conf") - .arg("--ro-bind").arg("/etc/localtime").arg("/etc/localtime") - .arg("--ro-bind").arg(*DIST_TLS).arg("/etc/ssl/certs/ca-certificates.crt") - .arg("--bind").arg(*LOG_LOCATION).arg("/mnt/share/pacwrap.log") - .arg("--bind").arg(ins.vars().pacman_gnupg()).arg("/mnt/share/gnupg") - .arg("--bind").arg(ins.vars().pacman_cache()).arg("/mnt/share/cache") - .arg("--ro-bind").arg(env!("PACWRAP_DIST_REPO")).arg("/mnt/share/dist-repo") - .arg("--dev").arg("/dev") - .arg("--dev").arg("/mnt/fs/dev") - .arg("--proc").arg("/mnt/fs/proc") - .arg("--unshare-all") - .arg("--share-net") - .arg("--clearenv") - .arg("--hostname").arg("pacwrap-agent") - .arg("--new-session") - .arg("--setenv").arg("HOME").arg("/tmp") - .arg("--setenv").arg("PATH").arg("/bin") - .arg("--setenv").arg("TERM").arg(*TERM) - .arg("--setenv").arg("LANG").arg(*LANG) - .arg("--setenv").arg("COLORTERM").arg(*COLORTERM) + match Command::new(BWRAP_EXECUTABLE).env_clear() + .arg("--bind").arg(&ins.vars().root()).arg("/mnt/fs") + .arg("--symlink").arg("/mnt/fs/usr").arg("/usr") + .arg("--ro-bind").arg(format!("{}/lib", *DIST_IMG)).arg("/lib64") + .arg("--ro-bind").arg(format!("{}/bin", *DIST_IMG)).arg("/bin") + .arg("--ro-bind").arg("/etc/resolv.conf").arg("/etc/resolv.conf") + .arg("--ro-bind").arg("/etc/localtime").arg("/etc/localtime") + .arg("--ro-bind").arg(*DIST_TLS).arg("/etc/ssl/certs/ca-certificates.crt") + .arg("--bind").arg(*LOG_LOCATION).arg("/mnt/share/pacwrap.log") + .arg("--bind").arg(ins.vars().pacman_gnupg()).arg("/mnt/share/gnupg") + .arg("--bind").arg(ins.vars().pacman_cache()).arg("/mnt/share/cache") + .arg("--ro-bind").arg(env!("PACWRAP_DIST_REPO")).arg("/mnt/share/dist-repo") + .arg("--dev").arg("/dev") + .arg("--dev").arg("/mnt/fs/dev") + .arg("--proc").arg("/mnt/fs/proc") + .arg("--unshare-all") + .arg("--share-net") + .arg("--hostname").arg("pacwrap-agent") + .arg("--new-session") + .arg("--setenv").arg("HOME").arg("/tmp") + .arg("--setenv").arg("PATH").arg("/bin") + .arg("--setenv").arg("TERM").arg(*TERM) + .arg("--setenv").arg("LANG").arg(*LANG) + .arg("--setenv").arg("COLORTERM").arg(*COLORTERM) .arg("--setenv").arg("LD_LIBRARY_PATH").arg("/lib64:/usr/lib") - .arg("--setenv").arg("LD_PRELOAD").arg("/lib64/libfakeroot.so:/lib64/libfakechroot.so") - .arg("--setenv").arg("PACWRAP_REAL_UID").arg(ID.0) - .arg("--setenv").arg("PACWRAP_REAL_GID").arg(ID.1) + .arg("--setenv").arg("LD_PRELOAD").arg("/lib64/libfakeroot.so:/lib64/libfakechroot.so") + .arg("--setenv").arg("PACWRAP_REAL_UID").arg(ID.0) + .arg("--setenv").arg("PACWRAP_REAL_GID").arg(ID.1) .arg("--setenv").arg("RUST_BACKTRACE").arg("1") - .arg("--die-with-parent") - .arg("--unshare-user") - .arg("--disable-userns") - .arg("--seccomp") - .arg(sec_fd.to_string()) - .arg("--ro-bind-data") - .arg(params_fd.to_string()) - .arg("/mnt/agent_params") - .arg("agent") - .arg("transact") - .fd_mappings(fd_mappings) - .unwrap() - .spawn() { - Ok(child) => Ok(child), - Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())), - } + .arg("--die-with-parent") + .arg("--unshare-user") + .arg("--disable-userns") + .arg("--seccomp") + .arg(sec_fd.to_string()) + .arg("--ro-bind-data") + .arg(params_fd.to_string()) + .arg("/mnt/agent_params") + .arg("agent") + .arg("transact") + .fd_mappings(fd_mappings) + .unwrap() + .spawn() + { + Ok(child) => Ok(child), + Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())), + } } pub fn pacman_key(path: &str, cmd: Vec<&str>) -> Result<()> { match Command::new(PACMAN_KEY_SCRIPT) - .stderr(Stdio::null()) + .stderr(Stdio::null()) .env("EUID", "0") .arg("--gpgdir") .arg(path) .args(cmd) - .spawn() { + .spawn() + { Ok(proc) => wait_on_process(PACMAN_KEY_SCRIPT, proc), Err(error) => err!(ErrorKind::ProcessInitFailure(PACMAN_KEY_SCRIPT, error.kind()))?, } diff --git a/pacwrap-core/src/exec/args.rs b/pacwrap-core/src/exec/args.rs index ffcfc69..30e6118 100644 --- a/pacwrap-core/src/exec/args.rs +++ b/pacwrap-core/src/exec/args.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,34 +17,37 @@ * along with this program. If not, see . */ -use std::{fmt::{Formatter, Debug}, collections::HashMap}; +use std::{ + collections::HashMap, + fmt::{Debug, Formatter}, +}; pub struct ExecutionArgs { bind: Vec, dev: Vec, env: Vec, dbus: Vec, - vars: HashMap, + vars: HashMap, } //TODO: This entire structure needs to be rethought impl ExecutionArgs { pub fn new() -> Self { - Self { - bind: Vec::new(), - dev: Vec::new(), - env: Vec::new(), + Self { + bind: Vec::new(), + dev: Vec::new(), + env: Vec::new(), dbus: Vec::new(), vars: HashMap::new(), } } - pub fn dir(&mut self, dest: impl Into) { + pub fn dir(&mut self, dest: impl Into) { self.bind.push("--dir".into()); self.bind.push(dest.into()); } - pub fn bind(&mut self, src: impl Into, dest: impl Into) { + pub fn bind(&mut self, src: impl Into, dest: impl Into) { self.bind.push("--bind".into()); self.bind.push(src.into()); self.bind.push(dest.into()); @@ -84,27 +87,27 @@ impl ExecutionArgs { self.dbus.push(format!("--{}={}", per.into(), socket.into())); } - pub fn push_env(&mut self, src: impl Into) { - self.env.push(src.into()); + pub fn push_env(&mut self, src: impl Into) { + self.env.push(src.into()); } - pub fn get_bind(&self) -> &Vec { - &self.bind + pub fn get_bind(&self) -> &Vec { + &self.bind } - pub fn get_dev(&self) -> &Vec { - &self.dev + pub fn get_dev(&self) -> &Vec { + &self.dev } - pub fn get_env(&self) -> &Vec { + pub fn get_env(&self) -> &Vec { &self.env } - pub fn get_dbus(&self) -> &Vec { - &self.dbus + pub fn get_dbus(&self) -> &Vec { + &self.dbus } - //TODO: Temporary workaround until structure is rebuilt + //TODO: Temporary workaround until structure is rebuilt pub fn get_var(&self, key: &str) -> Option<&String> { self.vars.get(key) } @@ -112,11 +115,11 @@ impl ExecutionArgs { impl Debug for ExecutionArgs { fn fmt(&self, fmter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - writeln!(fmter, "bind: {:?}", self.bind)?; + writeln!(fmter, "bind: {:?}", self.bind)?; writeln!(fmter, "env: {:?}", self.env)?; if self.dev.len() > 0 { - writeln!(fmter, "dev: {:?}", self.dev)?; + writeln!(fmter, "dev: {:?}", self.dev)?; } if self.dbus.len() > 0 { diff --git a/pacwrap-core/src/exec/seccomp.rs b/pacwrap-core/src/exec/seccomp.rs index 9c8222f..49d656b 100644 --- a/pacwrap-core/src/exec/seccomp.rs +++ b/pacwrap-core/src/exec/seccomp.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,14 +19,16 @@ use std::os::fd::AsRawFd; -use nix::libc; -use libseccomp::{ScmpArgCompare as Compare, - ScmpAction as Action, +use libseccomp::{ + ScmpAction as Action, + ScmpArch, + ScmpArgCompare as Compare, ScmpCompareOp as Op, - ScmpSyscall as Syscall, ScmpFilterContext, - ScmpArch}; -use os_pipe::{PipeWriter, PipeReader}; + ScmpSyscall as Syscall, +}; +use nix::libc; +use os_pipe::{PipeReader, PipeWriter}; use crate::config::instance::InstanceRuntime; @@ -40,72 +42,76 @@ pub enum FilterType { } static EPERM: Action = Action::Errno(libc::EPERM); -static ENOSYS: Action = Action::Errno(libc::ENOSYS); +static ENOSYS: Action = Action::Errno(libc::ENOSYS); -/* +/* * Personality values obtained from personality.h in the Linux kernel * * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/linux/personality.h */ -static PERSONALITY: u64 = if cfg!(target_pointer_width = "64") { 0x0000 } else { 0x0000 | 0x0800000 }; +static PERSONALITY: u64 = if cfg!(target_pointer_width = "64") { + 0x0000 +} else { + 0x0000 | 0x0800000 +}; -/* +/* * Syscall blocklists derived from flatpak-run.c in the flatpak project. * * https://github.com/flatpak/flatpak/blob/main/common/flatpak-run.c#L1835 - * - * Please do not open issue reports, esplicitly regarding lessened security, regarding filters - * that of which can be toggled. When the relevant options are activated, users are warned of - * the potential ramifications of so doing. - * + * + * Please do not open issue reports, esplicitly regarding lessened security, regarding filters + * that of which can be toggled. When the relevant options are activated, users are warned of + * the potential ramifications of so doing. + * * This encumbers a great responsibility upon the user when exercising this great power. */ static RULES: [(FilterType, &'static str, Action); 28] = [ - (Standard, "syslog", EPERM), - (Standard, "uselib", EPERM), - (Standard, "acct", EPERM), + (Standard, "syslog", EPERM), + (Standard, "uselib", EPERM), + (Standard, "acct", EPERM), (Standard, "quotactl", EPERM), - (Standard, "add_key", EPERM), - (Standard, "keyctl", EPERM), - (Standard, "request_key", EPERM), - (Standard, "move_pages", EPERM), - (Standard, "mbind", EPERM), - (Standard, "get_mempolicy", EPERM), - (Standard, "set_mempolicy", EPERM), - (Standard, "migrate_pages", EPERM), - (Standard, "clone3", ENOSYS), - (Standard, "open_tree", ENOSYS), - (Standard, "move_mount", ENOSYS), + (Standard, "add_key", EPERM), + (Standard, "keyctl", EPERM), + (Standard, "request_key", EPERM), + (Standard, "move_pages", EPERM), + (Standard, "mbind", EPERM), + (Standard, "get_mempolicy", EPERM), + (Standard, "set_mempolicy", EPERM), + (Standard, "migrate_pages", EPERM), + (Standard, "clone3", ENOSYS), + (Standard, "open_tree", ENOSYS), + (Standard, "move_mount", ENOSYS), (Standard, "fsopen", ENOSYS), - (Standard, "fsconfig", ENOSYS), - (Standard, "fsmount", ENOSYS), - (Standard, "fspick", ENOSYS), - (Standard, "mount_setattr", ENOSYS), - (Standard, "perf_event_open", ENOSYS), + (Standard, "fsconfig", ENOSYS), + (Standard, "fsmount", ENOSYS), + (Standard, "fspick", ENOSYS), + (Standard, "mount_setattr", ENOSYS), + (Standard, "perf_event_open", ENOSYS), (Standard, "ptrace", ENOSYS), - (Namespaces, "unshare", EPERM), - (Namespaces, "setns", EPERM), - (Namespaces, "mount", EPERM), + (Namespaces, "unshare", EPERM), + (Namespaces, "setns", EPERM), + (Namespaces, "mount", EPERM), (Namespaces, "umount2", EPERM), (Namespaces, "pivot_root", EPERM), - (Namespaces, "chroot", EPERM), + (Namespaces, "chroot", EPERM), ]; static RULES_COND: [(FilterType, &'static str, Action, Compare); 4] = [ - (TtyControl, "ioctl", EPERM, Compare::new(1, Op::MaskedEqual(libc::TIOCLINUX), libc::TIOCLINUX)), + (TtyControl, "ioctl", EPERM, Compare::new(1, Op::MaskedEqual(libc::TIOCLINUX), libc::TIOCLINUX)), (TtyControl, "ioctl", EPERM, Compare::new(1, Op::MaskedEqual(libc::TIOCSTI), libc::TIOCSTI)), - (Namespaces, "clone", EPERM, Compare::new(0, Op::MaskedEqual(libc::CLONE_NEWUSER as u64), libc::CLONE_NEWUSER as u64)), - (Standard, "personality", EPERM, Compare::new(0, Op::NotEqual, PERSONALITY)) + (Namespaces, "clone", EPERM, Compare::new(0, Op::MaskedEqual(libc::CLONE_NEWUSER as u64), libc::CLONE_NEWUSER as u64)), + (Standard, "personality", EPERM, Compare::new(0, Op::NotEqual, PERSONALITY)), ]; -// Provide configuration parameters for berkley filtering program generation +// Provide configuration parameters for berkley filtering program generation pub fn configure_bpf_program(instance: &InstanceRuntime) -> Vec { let mut filters = vec![Standard]; - if ! instance.enable_userns() { + if !instance.enable_userns() { filters.push(Namespaces) } - if ! instance.retain_session() { + if !instance.retain_session() { filters.push(TtyControl) } @@ -113,16 +119,22 @@ pub fn configure_bpf_program(instance: &InstanceRuntime) -> Vec { } // Generate berkley packet filtering program to pass into the namespaces container -pub fn provide_bpf_program(types: Vec, reader: &PipeReader, mut writer: PipeWriter) -> Result> { +pub fn provide_bpf_program( + types: Vec, + reader: &PipeReader, + mut writer: PipeWriter, +) -> Result> { let mut filter = ScmpFilterContext::new_filter(Action::Allow)?; - let rules = RULES.iter() - .filter(|a| types.contains(&a.0)) - .map(|a| (a.1, a.2)) - .collect::>(); - let rules_cond = RULES_COND.iter() - .filter(|a| types.contains(&a.0)) - .map(|a| (a.1, a.2, a.3)) - .collect::>(); + let rules = RULES + .iter() + .filter(|a| types.contains(&a.0)) + .map(|a| (a.1, a.2)) + .collect::>(); + let rules_cond = RULES_COND + .iter() + .filter(|a| types.contains(&a.0)) + .map(|a| (a.1, a.2, a.3)) + .collect::>(); if cfg!(target_arch = "x86_64") { filter.add_arch(ScmpArch::X86)?; @@ -133,10 +145,10 @@ pub fn provide_bpf_program(types: Vec, reader: &PipeReader, mut writ for rule in rules { filter.add_rule(rule.1, Syscall::from_name(rule.0)?)?; - } - - for rule in rules_cond { - filter.add_rule_conditional(rule.1, Syscall::from_name(rule.0)?, &[rule.2])?; + } + + for rule in rules_cond { + filter.add_rule_conditional(rule.1, Syscall::from_name(rule.0)?, &[rule.2])?; } filter.export_bpf(&mut writer).unwrap(); diff --git a/pacwrap-core/src/exec/utils.rs b/pacwrap-core/src/exec/utils.rs index b47c1b3..3dee996 100644 --- a/pacwrap-core/src/exec/utils.rs +++ b/pacwrap-core/src/exec/utils.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,52 +17,61 @@ * along with this program. If not, see . */ -use std::{process::Child, io::Read, os::fd::AsRawFd}; +use std::{io::Read, os::fd::AsRawFd, process::Child}; use os_pipe::{PipeReader, PipeWriter}; use serde::Serialize; use serde_yaml::Value; -use crate::{err, - error::*, - ErrorKind, - constants::BWRAP_EXECUTABLE, +use crate::{ config::{InstanceHandle, CONFIG}, - sync::{SyncError, transaction::{TransactionParameters, - TransactionMetadata}, - DEFAULT_ALPM_CONF}}; + constants::BWRAP_EXECUTABLE, + err, + error::*, + sync::{ + transaction::{TransactionMetadata, TransactionParameters}, + SyncError, + DEFAULT_ALPM_CONF, + }, + ErrorKind, +}; pub fn execute_fakeroot_container(ins: &InstanceHandle, arguments: Vec<&str>) -> Result<()> { match super::fakeroot_container(ins, arguments)?.wait() { Ok(_) => Ok(()), - Err(err) => err!(ErrorKind::ProcessWaitFailure(BWRAP_EXECUTABLE, err.kind())) + Err(err) => err!(ErrorKind::ProcessWaitFailure(BWRAP_EXECUTABLE, err.kind())), } } -pub fn bwrap_json(mut reader: PipeReader, writer: PipeWriter) -> Result { +pub fn bwrap_json(mut reader: PipeReader, writer: PipeWriter) -> Result { let mut output = String::new(); - + drop(writer); - reader.read_to_string(&mut output).unwrap(); - + reader.read_to_string(&mut output).unwrap(); + match serde_yaml::from_str::(&output) { Ok(value) => match value["child-pid"].as_u64() { - Some(value) => Ok(value as i32), + Some(value) => Ok(value as i32), None => err!(ErrorKind::Message("Unable to acquire child pid from bwrap process.")), }, Err(_) => err!(ErrorKind::Message("Unable to acquire child pid from bwrap process.")), } } -pub fn agent_params(reader: &PipeReader, writer: &PipeWriter, params: &TransactionParameters, metadata: &TransactionMetadata) -> Result { - serialize(params, writer)?; +pub fn agent_params( + reader: &PipeReader, + writer: &PipeWriter, + params: &TransactionParameters, + metadata: &TransactionMetadata, +) -> Result { + serialize(params, writer)?; serialize(&*CONFIG, writer)?; serialize(&*DEFAULT_ALPM_CONF, writer)?; serialize(metadata, writer)?; Ok(reader.as_raw_fd()) } -fn serialize Serialize>(input: &T, file: &PipeWriter) -> Result<()> { +fn serialize Serialize>(input: &T, file: &PipeWriter) -> Result<()> { match bincode::serialize_into::<&PipeWriter, T>(file, input) { Ok(()) => Ok(()), Err(error) => err!(SyncError::TransactionFailure(format!("Agent data serialization failed: {}", error))), @@ -76,9 +85,9 @@ pub fn handle_process(name: &'static str, result: std::result::Result Result<()> { +pub fn wait_on_process(name: &'static str, mut child: Child) -> Result<()> { match child.wait() { Ok(_) => Ok(()), - Err(error) => err!(ErrorKind::ProcessWaitFailure(name, error.kind())) + Err(error) => err!(ErrorKind::ProcessWaitFailure(name, error.kind())), } } diff --git a/pacwrap-core/src/lib.rs b/pacwrap-core/src/lib.rs index d5dfa37..18696d1 100644 --- a/pacwrap-core/src/lib.rs +++ b/pacwrap-core/src/lib.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,17 +17,17 @@ * along with this program. If not, see . */ -use std::fmt::{Display, Formatter, Debug}; +use std::fmt::{Debug, Display, Formatter}; use crate::constants::{BOLD, RESET}; +pub mod config; +pub mod constants; +pub mod error; +pub mod exec; +pub mod log; pub mod sync; pub mod utils; -pub mod constants; -pub mod config; -pub mod log; -pub mod exec; -pub mod error; pub use error::*; @@ -36,32 +36,33 @@ pub enum ErrorKind { EnvVarUnset(&'static str), ProcessInitFailure(&'static str, std::io::ErrorKind), ProcessWaitFailure(&'static str, std::io::ErrorKind), - IOError(String, std::io::ErrorKind), + IOError(String, std::io::ErrorKind), Message(&'static str), Termios(nix::errno::Errno), - InstanceNotFound(String), - DependencyNotFound(String, String), + InstanceNotFound(String), + DependencyNotFound(String, String), LinkerUninitialized, ThreadPoolUninitialized, ElevatedPrivileges, } impl Display for ErrorKind { - fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { match self { + Self::DependencyNotFound(dep, ins) => + write!(fmter, "Instance '{}{}{}': Dependency {}{}{} not found.", *BOLD, ins, *RESET, *BOLD, dep, *RESET), Self::Message(err) => write!(fmter, "{}", err), Self::EnvVarUnset(var) => write!(fmter, "${}{var}{} is unset.", *BOLD, *RESET), - Self::ProcessInitFailure(exec, err) => write!(fmter, "Unable to initialize '{exec}': {err}"), - Self::ProcessWaitFailure(exec, err) => write!(fmter, "Unable to wait on '{exec}': {err}"), + Self::ProcessInitFailure(exec, err) => write!(fmter, "Unable to initialize '{exec}': {err}"), + Self::ProcessWaitFailure(exec, err) => write!(fmter, "Unable to wait on '{exec}': {err}"), Self::InstanceNotFound(ins) => write!(fmter, "Instance {}{ins}{} not found.", *BOLD, *RESET), - Self::DependencyNotFound(dep,ins) => write!(fmter, "Dependency {}{dep}{} not found for {}{ins}{}.", *BOLD, *RESET, *BOLD, *RESET), - Self::IOError(ins, error) => write!(fmter, "'{ins}': {error}"), + Self::IOError(ins, error) => write!(fmter, "'{ins}': {error}"), Self::ThreadPoolUninitialized => write!(fmter, "Threadpool uninitialized"), - Self::LinkerUninitialized => write!(fmter, "Filesystem synchronization structure is uninitialized."), - Self::Termios(errno) => write!(fmter, "Failed to restore termios parameters: {errno}."), + Self::LinkerUninitialized => write!(fmter, "Filesystem synchronization structure is uninitialized."), + Self::Termios(errno) => write!(fmter, "Failed to restore termios parameters: {errno}."), Self::ElevatedPrivileges => write!(fmter, "Execution with elevated privileges is not supported."), }?; - + if let Self::Message(_) = self { write!(fmter, "\nTry 'pacwrap -h' for more information on valid operational parameters.")?; } @@ -71,9 +72,10 @@ impl Display for ErrorKind { } impl ErrorTrait for ErrorKind { - fn code(&self) -> i32 { + fn code(&self) -> i32 { match self { - ErrorKind::IOError(_,_) => 2, _ => 1, + ErrorKind::IOError(..) => 2, + _ => 1, } } } diff --git a/pacwrap-core/src/log.rs b/pacwrap-core/src/log.rs index de37add..d344775 100644 --- a/pacwrap-core/src/log.rs +++ b/pacwrap-core/src/log.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,23 +17,19 @@ * along with this program. If not, see . */ -use std::{path::Path, - fs::{OpenOptions, File}, - io::Write, fmt::{Display, Formatter}}; +use std::{ + fmt::{Display, Formatter}, + fs::{File, OpenOptions}, + io::Write, + path::Path, +}; -use time::{OffsetDateTime, - format_description::FormatItem, - macros::format_description, UtcOffset}; +use time::{format_description::FormatItem, macros::format_description, OffsetDateTime, UtcOffset}; -use crate::{err, - impl_error, - Error, - ErrorKind, - ErrorTrait, - Result, - constants::LOG_LOCATION}; +use crate::{constants::LOG_LOCATION, err, impl_error, Error, ErrorKind, ErrorTrait, Result}; -const DATE_FORMAT: &[FormatItem<'static>] = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour][offset_minute]"); +const DATE_FORMAT: &[FormatItem<'static>] = + format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour][offset_minute]"); const UTC_OFFSET: &[FormatItem<'static>] = format_description!("[offset_hour]"); impl_error!(LoggerError); @@ -58,20 +54,20 @@ pub struct Logger { } impl Logger { - pub fn new(module_name: &'static str) -> Self { - /* - * In order to deal with the potentiality of a race condition occurring - * between libalpm and the time crate, we cache the offset during the - * initalisation of this struct. - */ + pub fn new(module_name: &'static str) -> Self { + /* + * In order to deal with the potentiality of a race condition occurring + * between libalpm and the time crate, we cache the offset during the + * initalisation of this struct. + */ let ofs = OffsetDateTime::now_local() .unwrap_or(OffsetDateTime::now_utc()) .format(UTC_OFFSET) .unwrap(); let ofs = UtcOffset::parse(ofs.as_str(), UTC_OFFSET).unwrap(); - Self { - file: None, + Self { + file: None, module: module_name, offset: ofs, } @@ -79,29 +75,24 @@ impl Logger { pub fn init(mut self) -> Result { let path = Path::new(*LOG_LOCATION); - let file = OpenOptions::new() - .create(true) - .write(true) - .append(true) - .truncate(false) - .open(path); + let file = OpenOptions::new().create(true).write(true).append(true).truncate(false).open(path); self.file = Some(match file { Ok(file) => file, - Err(error) => err!(ErrorKind::IOError(LOG_LOCATION.to_string(), error.kind()))?, + Err(error) => err!(ErrorKind::IOError(LOG_LOCATION.to_string(), error.kind()))?, }); Ok(self) } - pub fn log(&mut self, msg: impl Into + std::fmt::Display) -> Result<()> { - /* - * We then attempt to update it here. - * - * If that fails, we use the previously cached value. This compromise ensures - * a stale offset value will eventually be updated to reflect the system's - * time offset if a change were to occur whilst this application is running. - */ + pub fn log(&mut self, msg: impl Into + std::fmt::Display) -> Result<()> { + /* + * We then attempt to update it here. + * + * If that fails, we use the previously cached value. This compromise ensures + * a stale offset value will eventually be updated to reflect the system's + * time offset if a change were to occur whilst this application is running. + */ if let Ok(local) = OffsetDateTime::now_local() { self.offset = UtcOffset::parse(local.format(UTC_OFFSET).unwrap().as_str(), UTC_OFFSET).unwrap(); } @@ -109,7 +100,7 @@ impl Logger { let time: OffsetDateTime = OffsetDateTime::now_utc().to_offset(self.offset); let write = match self.file.as_mut() { Some(file) => file.write(format!("[{}] [{}] {}\n", time.format(DATE_FORMAT).unwrap(), self.module, msg).as_bytes()), - None => err!(LoggerError::Uninitialized)? + None => err!(LoggerError::Uninitialized)?, }; match write { diff --git a/pacwrap-core/src/sync.rs b/pacwrap-core/src/sync.rs index 4c15d69..1bab8d7 100644 --- a/pacwrap-core/src/sync.rs +++ b/pacwrap-core/src/sync.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -16,36 +16,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -use std::{fmt::{Display, Formatter}, path::Path}; +use std::{ + fmt::{Display, Formatter}, + path::Path, + process::exit, +}; -use alpm::{Alpm, SigLevel, Usage}; +use alpm::{Alpm, SigLevel, Usage}; use lazy_static::lazy_static; use pacmanconf; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; -use crate::{err, - impl_error, +use crate::{ + config::{cache::InstanceCache, global::ProgressKind, Global, InsVars, InstanceHandle, CONFIG}, + constants::{ARROW_RED, BAR_GREEN, BOLD, CACHE_DIR, CONFIG_DIR, DATA_DIR, RESET}, + err, + error, error::*, - ErrorKind, - utils::print_warning, exec::pacman_key, - constants::{BAR_GREEN, RESET, BOLD, CACHE_DIR, DATA_DIR, CONFIG_DIR, ARROW_RED}, + impl_error, sync::event::download::{self, DownloadEvent}, - config::{InsVars, - InstanceHandle, - cache::InstanceCache, CONFIG, Global, global::ProgressKind}}; + utils::print_warning, + ErrorKind, +}; pub mod event; -pub mod utils; -pub mod transaction; pub mod filesystem; mod resolver; mod resolver_local; +pub mod transaction; +pub mod utils; lazy_static! { - static ref PACMAN_CONF: pacmanconf::Config = pacmanconf::Config::from_file(format!("{}/repositories.conf", *CONFIG_DIR)).unwrap(); - static ref DEFAULT_SIGLEVEL: SigLevel = signature(&CONFIG.alpm().sig_level(), SigLevel::PACKAGE | SigLevel::DATABASE_OPTIONAL); pub static ref DEFAULT_ALPM_CONF: AlpmConfigData = AlpmConfigData::new(); + static ref PACMAN_CONF: pacmanconf::Config = load_repositories(); + static ref DEFAULT_SIGLEVEL: SigLevel = default_signature(); } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -67,36 +72,41 @@ pub enum SyncError { InternalError(String), NoCompatibleRemotes, UnableToLocateKeyrings, + RepoConfError(String, String), } impl_error!(SyncError); impl Display for SyncError { - fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { - Self::DependentContainerMissing(u) => write!(fmter, "Dependent container '{}{u}{}' is misconfigured or otherwise is missing.", *BOLD, *RESET), + fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + match self { + Self::DependentContainerMissing(u) => + write!(fmter, "Dependent container '{}{u}{}' is misconfigured or otherwise is missing.", *BOLD, *RESET), + Self::TargetNotAvailable(pkg) => + write!(fmter, "Target package {}{pkg}{}: Not available in sync databases.", *BOLD, *RESET), + Self::TargetUpstream(pkg) => + write!(fmter, "Target package {}{pkg}{}: Installed in upstream container.", *BOLD, *RESET), Self::RecursionDepthExceeded(u) => write!(fmter, "Recursion depth exceeded maximum of {}{u}{}.", *BOLD, *RESET), - Self::TargetNotInstalled(pkg) => write!(fmter, "Target package {}{pkg}{}: Not installed.", *BOLD, *RESET), - Self::TargetNotAvailable(pkg) => write!(fmter, "Target package {}{pkg}{}: Not available in sync databases.", *BOLD, *RESET), - Self::TargetUpstream(pkg) => write!(fmter, "Target package {}{pkg}{}: Installed in upstream container.", *BOLD, *RESET), + Self::NoCompatibleRemotes => write!(fmter, "No compatible containers available to synchronize remote database."), + Self::InvalidMagicNumber => write!(fmter, "Deserialization of input parameters failed: Invalid magic number."), + Self::TargetNotInstalled(pkg) => write!(fmter, "Target package {}{pkg}{}: Not installed.", *BOLD, *RESET), Self::InitializationFailure(msg) => write!(fmter, "Failure to initialize transaction: {msg}"), Self::PreparationFailure(msg) => write!(fmter, "Failure to prepare transaction: {msg}"), Self::TransactionFailure(msg) => write!(fmter, "Failure to commit transaction: {msg}"), Self::DeserializationFailure => write!(fmter, "Deserialization of input parameters failed."), Self::ParameterAcquisitionFailure => write!(fmter, "Failure to acquire agent runtime parameters."), Self::AgentVersionMismatch => write!(fmter, "Agent binary mismatch."), - Self::NoCompatibleRemotes => write!(fmter, "No compatible containers available to synchronize remote database."), - Self::InvalidMagicNumber => write!(fmter, "Deserialization of input parameters failed: Invalid magic number."), Self::InternalError(msg) => write!(fmter, "Internal failure: {msg}"), Self::UnableToLocateKeyrings => write!(fmter, "Unable to locate pacman keyrings."), + Self::RepoConfError(path, err) => write!(fmter, "'{}': {}", path, err), Self::NothingToDo(_) => write!(fmter, "Nothing to do."), _ => Ok(()), }?; - //Temporary until command and control pipeline is implemented for pacwrap-agent + //Temporary until command and control pipeline is implemented for pacwrap-agent match self { - Self::TransactionFailureAgent => Ok(()), - _ => write!(fmter, "\n{} Transaction failed.", *ARROW_RED) + Self::TransactionFailureAgent => Ok(()), + _ => write!(fmter, "\n{} Transaction failed.", *ARROW_RED), } } } @@ -126,50 +136,55 @@ impl AlpmConfigData { remotes.push((repo.name.clone(), signature(&repo.sig_level, *DEFAULT_SIGLEVEL).bits(), repo.servers.clone())); } - remotes.push(("pacwrap".into(), - (SigLevel::PACKAGE_MARGINAL_OK | SigLevel::DATABASE_MARGINAL_OK).bits(), - vec![format!("file://{}", env!("PACWRAP_DIST_REPO")), format!("file:///mnt/share/dist-repo/")])); - - Self { - repos: remotes, - } + remotes.push(( + "pacwrap".into(), + (SigLevel::PACKAGE_MARGINAL_OK | SigLevel::DATABASE_MARGINAL_OK).bits(), + vec![ + format!("file://{}", env!("PACWRAP_DIST_REPO")), + format!("file:///mnt/share/dist-repo/"), + ], + )); + + Self { repos: remotes } } } pub fn instantiate_alpm_agent(config: &Global, remotes: &AlpmConfigData) -> Alpm { let mut handle = Alpm::new("/mnt/fs", "/mnt/fs/var/lib/pacman/").unwrap(); - handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); - handle.set_hookdirs(vec!["/mnt/fs/usr/share/libalpm/hooks/", "/mnt/fs/etc/pacman.d/hooks/"].iter()).unwrap(); + handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); + handle + .set_hookdirs(vec!["/mnt/fs/usr/share/libalpm/hooks/", "/mnt/fs/etc/pacman.d/hooks/"].iter()) + .unwrap(); handle.set_cachedirs(vec!["/mnt/share/cache"].iter()).unwrap(); handle.set_gpgdir("/mnt/share/gnupg").unwrap(); handle.set_logfile("/mnt/share/pacwrap.log").unwrap(); handle.set_check_space(false); handle.set_disable_dl_timeout(config.alpm().download_timeout()); handle.set_parallel_downloads(config.alpm().parallel_downloads()); - handle = register_remote(handle, remotes); + handle = register_remote(handle, remotes); handle } -pub fn instantiate_alpm(inshandle: &InstanceHandle) -> Alpm { +pub fn instantiate_alpm(inshandle: &InstanceHandle) -> Alpm { alpm_handle(inshandle.vars(), format!("{}/var/lib/pacman/", inshandle.vars().root()), &*DEFAULT_ALPM_CONF) } -fn alpm_handle(insvars: &InsVars, db_path: String, remotes: &AlpmConfigData) -> Alpm { +fn alpm_handle(insvars: &InsVars, db_path: String, remotes: &AlpmConfigData) -> Alpm { let root = insvars.root(); let mut handle = Alpm::new(root, &db_path).unwrap(); handle.set_cachedirs(vec![format!("{}/pkg", *CACHE_DIR)].iter()).unwrap(); handle.set_gpgdir(format!("{}/pacman/gnupg", *DATA_DIR)).unwrap(); handle.set_logfile(format!("{}/pacwrap.log", *DATA_DIR)).unwrap(); - handle.set_parallel_downloads(CONFIG.alpm().parallel_downloads()); + handle.set_parallel_downloads(CONFIG.alpm().parallel_downloads()); handle.set_check_space(CONFIG.alpm().check_space()); handle.set_disable_dl_timeout(CONFIG.alpm().download_timeout()); - handle = register_remote(handle, remotes); + handle = register_remote(handle, remotes); handle } -//TODO: Port pacman-key to Rust +//TODO: Port pacman-key to Rust pub fn instantiate_trust() -> Result<()> { let path = &format!("{}/pacman/gnupg/", *DATA_DIR); @@ -180,7 +195,7 @@ pub fn instantiate_trust() -> Result<()> { println!("{} {}Initializing package trust database...{}", *BAR_GREEN, *BOLD, *RESET); - if ! Path::new("/usr/share/pacman/keyrings").exists() { + if !Path::new("/usr/share/pacman/keyrings").exists() { err!(SyncError::UnableToLocateKeyrings)? } @@ -192,10 +207,9 @@ pub fn instantiate_trust() -> Result<()> { pacman_key(path, vec!["--populate"]) } -fn register_remote(mut handle: Alpm, config: &AlpmConfigData) -> Alpm { +fn register_remote(mut handle: Alpm, config: &AlpmConfigData) -> Alpm { for repo in &config.repos { - let core = handle.register_syncdb_mut(repo.0.clone(), - SigLevel::from_bits(repo.1).unwrap()).unwrap(); + let core = handle.register_syncdb_mut(repo.0.clone(), SigLevel::from_bits(repo.1).unwrap()).unwrap(); for server in &repo.2 { core.add_server(server.as_str()).unwrap(); @@ -208,32 +222,32 @@ fn register_remote(mut handle: Alpm, config: &AlpmConfigData) -> Alpm { } fn synchronize_database(cache: &InstanceCache, force: bool) -> Result<()> { - match cache.obtain_base_handle() { + match cache.obtain_base_handle() { Some(ins) => { let db_path = format!("{}/pacman/", *DATA_DIR); let mut handle = alpm_handle(&ins.vars(), db_path, &*DEFAULT_ALPM_CONF); - println!("{} {}Synchronizing package databases...{}", *BAR_GREEN, *BOLD, *RESET); + println!("{} {}Synchronizing package databases...{}", *BAR_GREEN, *BOLD, *RESET); handle.set_dl_cb(DownloadEvent::new().style(&ProgressKind::Verbose), download::event); if let Err(err) = handle.syncdbs_mut().update(force) { err!(SyncError::InitializationFailure(err.to_string()))? } - - Alpm::release(handle).unwrap(); + + Alpm::release(handle).unwrap(); for i in cache.registered().iter() { let ins: &InstanceHandle = cache.get_instance(i).unwrap(); let vars: &InsVars = ins.vars(); - let src = &format!("{}/pacman/sync/pacwrap.db",*DATA_DIR); + let src = &format!("{}/pacman/sync/pacwrap.db", *DATA_DIR); let dest = &format!("{}/var/lib/pacman/sync/pacwrap.db", vars.root()); - + if let Err(error) = filesystem::create_hard_link(src, dest) { - print_warning(error); + print_warning(error); } for repo in PACMAN_CONF.repos.iter() { - let src = &format!("{}/pacman/sync/{}.db",*DATA_DIR, repo.name); + let src = &format!("{}/pacman/sync/{}.db", *DATA_DIR, repo.name); let dest = &format!("{}/var/lib/pacman/sync/{}.db", vars.root(), repo.name); if let Err(error) = filesystem::create_hard_link(src, dest) { print_warning(error); @@ -242,8 +256,8 @@ fn synchronize_database(cache: &InstanceCache, force: bool) -> Result<()> { } Ok(()) - }, - None => err!(SyncError::NoCompatibleRemotes), + } + None => err!(SyncError::NoCompatibleRemotes), } } @@ -252,25 +266,45 @@ fn signature(sigs: &Vec, default: SigLevel) -> SigLevel { let mut sig = SigLevel::empty(); for level in sigs { - sig = sig | if level == "Required" || level == "PackageRequired" { - SigLevel::PACKAGE - } else if level == "DatabaseRequired" || level == "DatabaseTrustedOnly" { - SigLevel::DATABASE - } else if level == "PackageOptional" { - SigLevel::PACKAGE_OPTIONAL - } else if level == "PackageTrustAll" { - SigLevel::PACKAGE_UNKNOWN_OK | SigLevel::DATABASE_MARGINAL_OK - } else if level == "DatabaseOptional" { - SigLevel::DATABASE_OPTIONAL - } else if level == "DatabaseTrustAll" { - SigLevel::DATABASE_UNKNOWN_OK | SigLevel::PACKAGE_MARGINAL_OK - } else { - SigLevel::empty() - } + sig = sig + | if level == "Required" || level == "PackageRequired" { + SigLevel::PACKAGE + } else if level == "DatabaseRequired" || level == "DatabaseTrustedOnly" { + SigLevel::DATABASE + } else if level == "PackageOptional" { + SigLevel::PACKAGE_OPTIONAL + } else if level == "PackageTrustAll" { + SigLevel::PACKAGE_UNKNOWN_OK | SigLevel::DATABASE_MARGINAL_OK + } else if level == "DatabaseOptional" { + SigLevel::DATABASE_OPTIONAL + } else if level == "DatabaseTrustAll" { + SigLevel::DATABASE_UNKNOWN_OK | SigLevel::PACKAGE_MARGINAL_OK + } else { + SigLevel::empty() + } } - sig + sig } else { default } } + +fn load_repositories() -> pacmanconf::Config { + let path = format!("{}/repositories.conf", *CONFIG_DIR); + + match pacmanconf::Config::from_file(&path) { + Ok(config) => config, + Err(error) => { + let error = error.to_string(); + let error = error.split("error: ").collect::>()[1].split("\n").collect::>()[0]; + let error = error!(SyncError::RepoConfError(path, error.to_string())); + + exit(error.error()); + } + } +} + +fn default_signature() -> SigLevel { + signature(&CONFIG.alpm().sig_level(), SigLevel::PACKAGE | SigLevel::DATABASE_OPTIONAL) +} diff --git a/pacwrap-core/src/sync/event.rs b/pacwrap-core/src/sync/event.rs index 94e99fe..24c2fb6 100644 --- a/pacwrap-core/src/sync/event.rs +++ b/pacwrap-core/src/sync/event.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -18,23 +18,23 @@ */ pub mod download; -pub mod query; pub mod progress; +pub mod query; fn whitespace(total: usize, current: usize) -> String { let total = log10(total); - let current = log10(current); + let current = log10(current); let mut whitespace = String::new(); - let difference = total-current; - - for _ in 0..difference { + let difference = total - current; + + for _ in 0 .. difference { whitespace.push_str(" "); - } + } whitespace } -fn log10(mut value: usize) -> usize { +fn log10(mut value: usize) -> usize { let mut length = 0; while value > 0 { diff --git a/pacwrap-core/src/sync/event/download.rs b/pacwrap-core/src/sync/event/download.rs index fe4bee0..88f95b2 100644 --- a/pacwrap-core/src/sync/event/download.rs +++ b/pacwrap-core/src/sync/event/download.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,38 +21,42 @@ use std::collections::HashMap; use alpm::{AnyDownloadEvent, DownloadEvent as Event}; use dialoguer::console::Term; -use indicatif::{MultiProgress, ProgressBar, ProgressStyle, ProgressDrawTarget}; +use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; use lazy_static::lazy_static; use simplebyteunit::simplebyteunit::*; -use crate::{constants::{ARROW_CYAN, BOLD, RESET}, config::global::ProgressKind, sync::transaction::TransactionMode}; +use crate::{ + config::global::ProgressKind, + constants::{ARROW_CYAN, BOLD, RESET}, + sync::transaction::TransactionMode, +}; use super::whitespace; -lazy_static!{ - static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}").unwrap(); - static ref UP_TO_DATE: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg} is up-to-date!") +lazy_static! { + static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}").unwrap(); + static ref UP_TO_DATE: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg} is up-to-date!") .unwrap() .progress_chars("#-") - .tick_strings(&[" ","✓"]); + .tick_strings(&[" ", "✓"]); } #[derive(Clone)] pub struct DownloadEvent { total: usize, position: usize, - total_bar: Option, + total_bar: Option, condensed: bool, progress: MultiProgress, bars: HashMap<&'static str, ProgressBar>, - style: Option, + style: Option, } impl DownloadEvent { - pub fn new() -> Self { + pub fn new() -> Self { Self { total: 0, - position: 0, + position: 0, total_bar: None, condensed: false, progress: MultiProgress::new(), @@ -62,17 +66,20 @@ impl DownloadEvent { } pub fn style(mut self, kind: &ProgressKind) -> Self { - self.style = match kind { + self.style = match kind { ProgressKind::Simple => None, _ => Some({ let size = Term::size(&Term::stdout()); let width = ((size.1 / 2) - 14).to_string(); - ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned()+&width - + "} {bytes:>11} {bytes_per_sec:>12} {elapsed_precise:>5} [{wide_bar}] {percent:<3}%")) - .unwrap() - .progress_chars("#-") - .tick_strings(&[" ", "✓"]) + ProgressStyle::with_template( + &(" {spinner:.green} {msg:<".to_owned() + + &width + + "} {bytes:>11} {bytes_per_sec:>12} {elapsed_precise:>5} [{wide_bar}] {percent:<3}%"), + ) + .unwrap() + .progress_chars("#-") + .tick_strings(&[" ", "✓"]) }), }; self @@ -82,10 +89,13 @@ impl DownloadEvent { self.total = files; self.total_bar = match (bytes > 0 && files > 1, self.style.as_ref()) { (true, Some(style)) => Some({ - let bar = self.progress.add(ProgressBar::new(bytes) - .with_style(style.clone()) - .with_message(format!("Total ({}0/{})", whitespace(files, 1), files))); - + let bar = ProgressBar::new(bytes).with_style(style.clone()).with_message(format!( + "Total ({}0/{})", + whitespace(files, 1), + files + )); + let bar = self.progress.add(bar); + bar.set_position(0); bar }), @@ -113,34 +123,35 @@ impl DownloadEvent { fn increment(&mut self, progress: u64) { let bar = match self.total_bar.as_mut() { - Some(bar) => bar, None => return, + Some(bar) => bar, + None => return, }; self.position += 1; - + let total = self.total; let pos = self.position; let whitespace = whitespace(total, pos); bar.inc(progress); - bar.set_message(format!("Total ({}{}/{})", whitespace, pos, total)); - - if total == pos { - bar.finish(); - } + bar.set_message(format!("Total ({}{}/{})", whitespace, pos, total)); + + if total == pos { + bar.finish(); + } } - fn insert(&mut self, file: &str) { - let pb = match self.total_bar.as_mut() { + fn insert(&mut self, file: &str) { + let pb = match self.total_bar.as_mut() { Some(total) => match self.condensed { - true => self.progress.insert_after(&total, ProgressBar::new(0)), + true => self.progress.insert_after(&total, ProgressBar::new(0)), false => self.progress.insert_before(&total, ProgressBar::new(0)), - }, - None => self.progress.add(ProgressBar::new(0)) + }, + None => self.progress.add(ProgressBar::new(0)), }; pb.set_style(INIT.clone()); - pb.set_message(message(file)); + pb.set_message(message(file)); /* * alpm-rs expects our callback signature to provide a struct bound by a 'static lifetime, @@ -152,7 +163,7 @@ impl DownloadEvent { pub fn simple(file: &str, download: AnyDownloadEvent, this: &mut DownloadEvent) { if file.ends_with(".sig") { - return; + return; } if let Event::Completed(progress) = download.event() { @@ -161,72 +172,73 @@ pub fn simple(file: &str, download: AnyDownloadEvent, this: &mut DownloadEvent) let size = progress.total.abs().to_byteunit(SI); let total = this.total; let pos = this.position; - let whitespace = whitespace(total, pos); + let whitespace = whitespace(total, pos); let message = message(file); - eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) {message} downloaded ({size})", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET); + eprintln!( + "{} ({}{whitespace}{pos}{}/{}{total}{}) {message} downloaded ({size})", + *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET + ); } } pub fn event(file: &str, download: AnyDownloadEvent, this: &mut DownloadEvent) { if file.ends_with(".sig") { - return; + return; } match download.event() { - Event::Progress(progress) => { + Event::Progress(progress) => if let Some(pb) = this.bars.get_mut(file) { if pb.length().unwrap() == 0 { pb.set_length(progress.total.unsigned_abs()); pb.set_style(this.style.as_ref().unwrap().clone()); } - pb.set_position(progress.downloaded.unsigned_abs()); - } - }, - Event::Completed(progress) => { - if let Some(pb) = this.bars.remove(file) { - if pb.length().unwrap() == 0 { + pb.set_position(progress.downloaded.unsigned_abs()); + }, + Event::Completed(progress) => + if let Some(pb) = this.bars.remove(file) { + if pb.length().unwrap() == 0 { pb.set_style(UP_TO_DATE.clone()); } - - pb.finish(); + + pb.finish(); this.increment(progress.total.unsigned_abs()); if this.condensed { pb.set_draw_target(ProgressDrawTarget::hidden()); } - } - }, + }, Event::Init(progress) => { if progress.optional { return; } this.insert(file); - }, - Event::Retry(_) => { - if let Some(pb) = this.bars.get_mut(file) { + } + Event::Retry(_) => + if let Some(pb) = this.bars.get_mut(file) { pb.set_position(0); pb.set_style(INIT.clone()); - } - }, + }, } } - pub fn callback(progress: &ProgressKind) -> for<'a, 'b, 'c> fn(file: &'a str, AnyDownloadEvent<'b>, this: &'c mut DownloadEvent) { +pub fn callback(progress: &ProgressKind) -> for<'a, 'b, 'c> fn(file: &'a str, AnyDownloadEvent<'b>, this: &'c mut DownloadEvent) { match progress { - ProgressKind::Simple => simple, _ => event + ProgressKind::Simple => simple, + _ => event, } } fn message(filename: &str) -> String { - let name: Vec<&str> = filename.split(".pkg.tar.").collect(); + let name: Vec<&str> = filename.split(".pkg.tar.").collect(); let mut msg_name: String = name[0].to_string(); if msg_name.ends_with(".db") { - msg_name.truncate(msg_name.len()-3); + msg_name.truncate(msg_name.len() - 3); } - + msg_name } diff --git a/pacwrap-core/src/sync/event/progress.rs b/pacwrap-core/src/sync/event/progress.rs index f7a7d3f..217a7a5 100644 --- a/pacwrap-core/src/sync/event/progress.rs +++ b/pacwrap-core/src/sync/event/progress.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -21,9 +21,14 @@ use alpm::Progress as Event; use dialoguer::console::Term; use indicatif::{ProgressBar, ProgressStyle}; -use crate::{constants::{BOLD, RESET, ARROW_CYAN}, - sync::{event::whitespace, transaction::{TransactionType, TransactionMode}}, - config::global::ProgressKind}; +use crate::{ + config::global::ProgressKind, + constants::{ARROW_CYAN, BOLD, RESET}, + sync::{ + event::whitespace, + transaction::{TransactionMode, TransactionType}, + }, +}; #[derive(Clone)] pub struct ProgressEvent { @@ -34,12 +39,12 @@ pub struct ProgressEvent { } impl ProgressEvent { - pub fn new() -> Self { + pub fn new() -> Self { Self { - current: None, - progress: None, + current: None, + progress: None, style: None, - offset: 0, + offset: 0, } } @@ -49,15 +54,16 @@ impl ProgressEvent { } pub fn style(mut self, kind: &ProgressKind) -> Self { - self.style = match kind { + self.style = match kind { ProgressKind::Simple => None, _ => Some({ let size = Term::size(&Term::stdout()); let width = (size.1 / 2).to_string(); - - ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned() - + &width +"} [{wide_bar}] {percent:<3}%")) - .unwrap().progress_chars("#-").tick_strings(&[" ", "✓"]) + + ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned() + &width + "} [{wide_bar}] {percent:<3}%")) + .unwrap() + .progress_chars("#-") + .tick_strings(&[" ", "✓"]) }), }; self @@ -65,11 +71,11 @@ impl ProgressEvent { fn bar(&mut self, ident: Option, name: &str, howmany: usize, current: usize, size: usize) { let pos = current + self.offset; - let total = howmany + self.offset; + let total = howmany + self.offset; let whitespace = whitespace(total, pos); let progress = ProgressBar::new(size as u64); - progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {name}", *BOLD, *RESET, *BOLD, *RESET)); + progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {name}", *BOLD, *RESET, *BOLD, *RESET)); progress.set_style(self.style.as_ref().unwrap().clone()); self.progress = Some(progress); @@ -78,46 +84,51 @@ impl ProgressEvent { } pub fn event(event: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) { - let ident = ident(event,pkgname); + let ident = ident(event, pkgname); match this.current.as_deref() { - Some(current_ident) => if ident != current_ident { - this.bar(Some(ident), &name(event,pkgname), howmany, current, 100) - }, - None => this.bar(Some(ident), &name(event,pkgname), howmany, current, 100), + Some(current_ident) => + if ident != current_ident { + this.bar(Some(ident), &name(event, pkgname), howmany, current, 100) + }, + None => this.bar(Some(ident), &name(event, pkgname), howmany, current, 100), }; let progress = match this.progress.as_mut() { - Some(progress) => progress, None => return, + Some(progress) => progress, + None => return, }; - + progress.set_position(percent as u64); if percent == 100 { - progress.finish(); + progress.finish(); } } pub fn simple(progress: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) { if percent == 0 { let pos = current + this.offset; - let total = howmany + this.offset; - let progress_name: String = name(progress,pkgname); + let total = howmany + this.offset; + let progress_name: String = name(progress, pkgname); let whitespace = whitespace(total, pos); - eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET); + eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET); if current == howmany { - eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) Synchronization complete", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET); + eprintln!( + "{} ({}{whitespace}{pos}{}/{}{total}{}) Synchronization complete", + *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET + ); } } } pub fn condensed(kind: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) { - if let Event::AddStart | Event::RemoveStart | Event::UpgradeStart = kind { + if let Event::AddStart | Event::RemoveStart | Event::UpgradeStart = kind { let pos = current + this.offset; - let total = howmany + this.offset; - let progress_name: String = name(kind,pkgname); + let total = howmany + this.offset; + let progress_name: String = name(kind, pkgname); let whitespace = whitespace(total, pos); if let Some(_) = this.current { @@ -125,16 +136,20 @@ pub fn condensed(kind: Event, pkgname: &str, percent: i32, howmany: usize, curre } let progress = match this.progress.as_mut() { - Some(progress) => progress, None => return, + Some(progress) => progress, + None => return, }; - progress.set_position(current as u64); + progress.set_position(current as u64); if current != howmany { - progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET)); + progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET)); } else { - progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) Synchronization complete", *BOLD, *RESET, *BOLD, *RESET)); - progress.finish(); + progress.set_message(format!( + "({}{whitespace}{pos}{}/{}{total}{}) Synchronization complete", + *BOLD, *RESET, *BOLD, *RESET + )); + progress.finish(); } } else { event(kind, pkgname, percent, howmany, current, this) @@ -142,13 +157,13 @@ pub fn condensed(kind: Event, pkgname: &str, percent: i32, howmany: usize, curre } fn name(progress: Event, pkgname: &str) -> String { - match progress { - Event::UpgradeStart => format!("Upgrading {}", pkgname), + match progress { + Event::UpgradeStart => format!("Upgrading {}", pkgname), Event::AddStart => format!("Installing {}", pkgname), Event::RemoveStart => format!("Removing {}", pkgname), Event::DowngradeStart => format!("Downgrading {}", pkgname), Event::ReinstallStart => format!("Reinstalling {}", pkgname), - Event::KeyringStart => format!("Loading keyring"), + Event::KeyringStart => format!("Loading keyring"), Event::IntegrityStart => format!("Checking integrity"), Event::LoadStart => format!("Loading packages"), Event::ConflictsStart => format!("Checking conflicts"), @@ -162,12 +177,15 @@ fn ident(progress: Event, pkgname: &str) -> String { Event::IntegrityStart => "integrity", Event::LoadStart => "loadstart", Event::ConflictsStart => "conflicts", - _ => pkgname - - }.to_owned() + _ => pkgname, + } + .to_owned() } -pub fn callback(state: &TransactionMode, kind: &ProgressKind) -> for<'a, 'b> fn(Event, &'a str, i32, usize, usize, &'b mut ProgressEvent) { +pub fn callback( + state: &TransactionMode, + kind: &ProgressKind, +) -> for<'a, 'b> fn(Event, &'a str, i32, usize, usize, &'b mut ProgressEvent) { match kind { ProgressKind::Simple => simple, ProgressKind::CondensedForeign => match state { diff --git a/pacwrap-core/src/sync/event/query.rs b/pacwrap-core/src/sync/event/query.rs index e2048f4..14dc05f 100644 --- a/pacwrap-core/src/sync/event/query.rs +++ b/pacwrap-core/src/sync/event/query.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -29,20 +29,20 @@ pub fn callback(question: AnyQuestion, _: &mut ()) { let pkg_a = x.conflict().package1(); let pkg_b = x.conflict().package2(); let prompt_string = format!("Conflict between {pkg_a} and {pkg_b}; Remove {pkg_b}?"); - + if let Ok(_) = prompt("->", prompt_string, false) { x.set_remove(true); } - }, - Replace(x) => { + } + Replace(x) => { let old = x.oldpkg().name(); let new = x.newpkg().name(); let prompt_string = format!("Replace package {old} with {new}?"); - + if let Ok(_) = prompt("->", prompt_string, false) { x.set_replace(true); } - }, + } Corrupted(mut x) => { let filepath = x.filepath(); let filename = Path::new(filepath).file_name().unwrap().to_str().unwrap(); @@ -52,18 +52,18 @@ pub fn callback(question: AnyQuestion, _: &mut ()) { if let Ok(_) = prompt("::", prompt_string, true) { x.set_remove(true); } - }, + } ImportKey(mut x) => { let key = x.key(); let fingerprint = key.fingerprint(); let email = key.email(); let name = key.name(); let prompt_string = format!("Import key {fingerprint},\"{name} <{email}>\" to keyring?"); - + if let Ok(_) = prompt("->", prompt_string, true) { x.set_import(true); - } - }, + } + } _ => (), } } diff --git a/pacwrap-core/src/sync/filesystem.rs b/pacwrap-core/src/sync/filesystem.rs index 667b0db..72a414e 100644 --- a/pacwrap-core/src/sync/filesystem.rs +++ b/pacwrap-core/src/sync/filesystem.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,27 +17,33 @@ * along with this program. If not, see . */ -use std::{fs::{self, File, Metadata}, +use std::{ + collections::{HashMap, HashSet}, + fs::{self, File, Metadata}, io::Read, - os::unix::fs::symlink, - os::unix::prelude::MetadataExt, + os::unix::{fs::symlink, prelude::MetadataExt}, path::Path, - sync::{Arc, mpsc::{Sender, self, Receiver}}, - collections::{HashMap, HashSet}}; + sync::{ + mpsc::{self, Receiver, Sender}, + Arc, + }, +}; use dialoguer::console::Term; -use rayon::{prelude::*, {ThreadPool, ThreadPoolBuilder}}; use indexmap::IndexMap; -use indicatif::{ProgressBar, ProgressStyle, ProgressDrawTarget}; -use serde::{Serialize, Deserialize}; +use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; +use rayon::{prelude::*, ThreadPool, ThreadPoolBuilder}; +use serde::{Deserialize, Serialize}; use walkdir::WalkDir; -use crate::{err, - ErrorKind, +use crate::{ + config::{InstanceCache, InstanceHandle, InstanceType::*}, + constants::{ARROW_CYAN, BAR_GREEN, BOLD, DATA_DIR, RESET}, + err, + utils::{print_error, print_warning, read_le_32}, Error, - config::{InstanceHandle, InstanceCache, InstanceType::*}, - constants::{RESET, BOLD, ARROW_CYAN, BAR_GREEN, DATA_DIR}, - utils::{print_warning, print_error, read_le_32}}; + ErrorKind, +}; static VERSION: u32 = 1; static MAGIC_NUMBER: u32 = 408948530; @@ -46,7 +52,7 @@ static MAGIC_NUMBER: u32 = 408948530; struct FileSystemState { magic: u32, version: u32, - files: IndexMap, (FileType, Arc)> + files: IndexMap, (FileType, Arc)>, } impl FileSystemState { @@ -73,7 +79,7 @@ impl From for FileType { 2 => Self::Directory, 1 => Self::SymLink, 0 => Self::HardLink, - _ => Self::Invalid(integer) + _ => Self::Invalid(integer), } } } @@ -96,27 +102,28 @@ enum SyncMessage { } pub struct FileSystemStateSync<'a> { - state_map: HashMap, FileSystemState>, + state_map: HashMap, FileSystemState>, state_map_prev: HashMap, FileSystemState>, linked: HashSet>, queued: HashSet<&'a str>, progress: ProgressBar, cache: &'a InstanceCache<'a>, pool: Option, - max_chars: u16, + max_chars: u16, } -impl <'a>FileSystemStateSync<'a> { +impl<'a> FileSystemStateSync<'a> { pub fn new(inscache: &'a InstanceCache) -> Self { let size = Term::size(&Term::stdout()); let column_half = size.1 / 2; - let style = ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned() - +column_half.to_string().as_str()+"} [{wide_bar}] {percent:<3}%")) - .unwrap() - .progress_chars("#-") - .tick_strings(&[">", "✓"]); + let style = ProgressStyle::with_template( + &(" {spinner:.green} {msg:<".to_owned() + column_half.to_string().as_str() + "} [{wide_bar}] {percent:<3}%"), + ) + .unwrap() + .progress_chars("#-") + .tick_strings(&[">", "✓"]); let pr = ProgressBar::new(0).with_style(style); - + pr.set_draw_target(ProgressDrawTarget::hidden()); Self { @@ -132,17 +139,21 @@ impl <'a>FileSystemStateSync<'a> { } pub fn engage(&mut self, containers: &Vec<&'a str>) -> Result<(), Error> { - let (tx, rx) = self.link(containers, mpsc::channel())?; - - drop(tx); + let (tx, rx) = self.link(containers, mpsc::channel())?; + + drop(tx); while let Ok(()) = rx.recv() {} Ok(()) } - - fn link(&mut self, containers: &Vec<&'a str>, mut write_chan: (Sender<()>, Receiver<()>)) -> Result<(Sender<()>, Receiver<()>), Error> { + + fn link( + &mut self, + containers: &Vec<&'a str>, + mut write_chan: (Sender<()>, Receiver<()>), + ) -> Result<(Sender<()>, Receiver<()>), Error> { let (tx, rx): (Sender, Receiver) = mpsc::channel(); - for ins in containers { + for ins in containers { if self.queued.contains(ins) { continue; } @@ -150,11 +161,11 @@ impl <'a>FileSystemStateSync<'a> { let inshandle = self.cache.get_instance(ins)?; write_chan = self.link(&inshandle.metadata().dependencies(), write_chan)?; - - if let ROOT = inshandle.metadata().container_type() { - self.link_instance(inshandle, tx.clone())?; + + if let Aggregate = inshandle.metadata().container_type() { + self.link_instance(inshandle, tx.clone())?; } else { - self.obtain_slice(inshandle, tx.clone())?; + self.obtain_slice(inshandle, tx.clone())?; } self.queued.insert(ins); @@ -165,18 +176,18 @@ impl <'a>FileSystemStateSync<'a> { Ok(write_chan) } - fn wait(&mut self, mut queue: HashSet<&'a str>, rx: Receiver, write_chan: &(Sender<()>, Receiver<()>)) { + fn wait(&mut self, mut queue: HashSet<&'a str>, rx: Receiver, write_chan: &(Sender<()>, Receiver<()>)) { while let Ok(recv) = rx.recv() { match recv { SyncMessage::LinkComplete(ins) => { let instance = ins.as_ref(); let status = queue_status(&queue, instance, self.max_chars as usize); - + queue.remove(instance); self.linked.insert(ins); self.progress.set_message(status); self.progress.inc(1); - }, + } SyncMessage::SaveState(dep, fs_state) => { if let Some(_) = self.state_map.get(&dep) { continue; @@ -185,30 +196,30 @@ impl <'a>FileSystemStateSync<'a> { if fs_state.files.len() == 0 { continue; } - + self.state_map.insert(dep.clone(), fs_state.clone()); - self.write(write_chan.0.clone(), fs_state, dep); + self.write(write_chan.0.clone(), fs_state, dep); } } - } + } } fn previous_state(&mut self, instance: &Arc) -> FileSystemState { if let Some(st) = self.state_map_prev.get(instance) { - return st.clone() + return st.clone(); } - let mut header_buffer = vec![0; 8]; + let mut header_buffer = vec![0; 8]; let path = format!("{}/state/{}.dat", *DATA_DIR, instance); - let mut file = match File::open(&path) { + let mut file = match File::open(&path) { Ok(file) => file, - Err(err) => { + Err(err) => { if err.kind() != std::io::ErrorKind::NotFound { print_error(format!("'{}': {}", path, err.kind())); } - return self.blank_state(instance); - }, + return self.blank_state(instance); + } }; if let Err(error) = file.read_exact(&mut header_buffer) { @@ -221,19 +232,24 @@ impl <'a>FileSystemStateSync<'a> { if magic != MAGIC_NUMBER { print_warning(format!("'{}{instance}{}.dat': Magic number mismatch ({MAGIC_NUMBER} != {magic})", *BOLD, *RESET)); - return self.blank_state(instance); - } else if version != VERSION { - return self.blank_state(instance); + return self.blank_state(instance); + } else if version != VERSION { + return self.blank_state(instance); } match bincode::deserialize_from::<&File, FileSystemState>(&file) { - Ok(state) => { + Ok(state) => { self.state_map_prev.insert(instance.clone(), state.clone()); state - }, - Err(err) => { - print_error(format!("Deserialization failure occurred with '{}{instance}{}.dat': {}", *BOLD, *RESET, err.as_ref())); - return self.blank_state(instance); + } + Err(err) => { + print_error(format!( + "Deserialization failure occurred with '{}{instance}{}.dat': {}", + *BOLD, + *RESET, + err.as_ref() + )); + return self.blank_state(instance); } } } @@ -241,8 +257,8 @@ impl <'a>FileSystemStateSync<'a> { fn blank_state(&mut self, instance: &Arc) -> FileSystemState { let state = FileSystemState::new(); - self.state_map_prev.insert(instance.clone(), state.clone()); - state + self.state_map_prev.insert(instance.clone(), state.clone()); + state } fn write(&mut self, tx: Sender<()>, ds: FileSystemState, dep: Arc) { @@ -250,26 +266,26 @@ impl <'a>FileSystemStateSync<'a> { let output = match File::create(path) { Ok(file) => file, Err(err) => { - print_warning(format!("Writing '{}': {}", path, err.kind())); + print_warning(format!("Writing '{}': {}", path, err.kind())); return; } }; - self.pool().unwrap().spawn(move ||{ + self.pool().unwrap().spawn(move || { if let Err(err) = bincode::serialize_into(output, &ds) { - print_error(format!("Serialization failure occurred with '{}{dep}{}.dat': {}", *BOLD, *RESET, err.to_string())); + print_error(format!("Serialization failure occurred with '{}{dep}{}.dat': {}", *BOLD, *RESET, err.to_string())); } drop(tx); }); - } + } fn obtain_slice(&mut self, inshandle: &InstanceHandle, tx: Sender) -> Result<(), Error> { let instance: Arc = inshandle.vars().instance().into(); let root = inshandle.vars().root().into(); - + self.previous_state(&instance); - Ok(self.pool()?.spawn(move ||{ + Ok(self.pool()?.spawn(move || { let mut state = FileSystemState::new(); obtain_state(root, &mut state); @@ -280,24 +296,24 @@ impl <'a>FileSystemStateSync<'a> { } fn link_instance(&mut self, inshandle: &InstanceHandle, tx: Sender) -> Result<(), Error> { - let mut map = Vec::new(); + let mut map = Vec::new(); let mut prev = Vec::new(); let instance: Arc = inshandle.vars().instance().into(); let root: Arc = inshandle.vars().root().into(); let state = FileSystemState::new(); - + for dep in inshandle.metadata().dependencies() { let dephandle = self.cache.get_instance(dep).unwrap(); - let state = match self.state_map.get(dep) { + let state = match self.state_map.get(dep) { Some(state) => state.clone(), - None => FileSystemState::new() + None => FileSystemState::new(), }; prev.push(self.previous_state(&Arc::from(dep.as_ref()))); map.push((dephandle.vars().root().into(), state)); } - Ok(self.pool()?.spawn(move ||{ + Ok(self.pool()?.spawn(move || { let state = filesystem_state(state, map); let state_prev = previous_state(prev); @@ -311,30 +327,29 @@ impl <'a>FileSystemStateSync<'a> { fn pool(&self) -> Result<&ThreadPool, Error> { match self.pool.as_ref() { - Some(pool) => Ok(pool), - None => err!(ErrorKind::ThreadPoolUninitialized) + Some(pool) => Ok(pool), + None => err!(ErrorKind::ThreadPoolUninitialized), } } pub fn prepare_single(&mut self) { - println!("{} Synchronizing container state...", *ARROW_CYAN); + println!("{} Synchronizing container state...", *ARROW_CYAN); if let None = self.pool { - self.pool = Some(ThreadPoolBuilder::new() - .thread_name(|f| { format!("PW-LINKER-{}", f) }) - .num_threads(2) - .build() - .unwrap()); + self.pool = Some( + ThreadPoolBuilder::new() + .thread_name(|f| format!("PW-LINKER-{}", f)) + .num_threads(2) + .build() + .unwrap(), + ); } } pub fn prepare(&mut self, length: usize) { - println!("{} {}Synchronizing container filesystems...{} ",*BAR_GREEN, *BOLD, *RESET); + println!("{} {}Synchronizing container filesystems...{} ", *BAR_GREEN, *BOLD, *RESET); - self.pool = Some(ThreadPoolBuilder::new() - .thread_name(|f| { format!("PW-LINKER-{}", f) }) - .build() - .unwrap()); + self.pool = Some(ThreadPoolBuilder::new().thread_name(|f| format!("PW-LINKER-{}", f)).build().unwrap()); self.progress.set_draw_target(ProgressDrawTarget::stdout()); self.progress.set_message("Synhcronizing containers.."); self.progress.set_position(0); @@ -346,7 +361,7 @@ impl <'a>FileSystemStateSync<'a> { } pub fn finish(&mut self) { - self.progress.set_message("Synchronization complete."); + self.progress.set_message("Synchronization complete."); self.progress.finish(); self.pool = None; } @@ -381,25 +396,23 @@ fn filesystem_state(mut state: FileSystemState, map: Vec<(Arc, FileSystemSt fn obtain_state(root: Arc, state: &mut FileSystemState) { let len = root.len(); - let entries = WalkDir::new(root.as_ref()) - .into_iter() - .filter_map(|e| e.ok()); + let entries = WalkDir::new(root.as_ref()).into_iter().filter_map(|e| e.ok()); - for entry in entries { + for entry in entries { let src: Arc = entry.path().to_str().unwrap().into(); let src_tr: Arc = src.split_at(len).1.into(); - + if let Some(_) = state.files.get(&src_tr) { continue; } - if src.contains("/var/lib/pacman") - || src.ends_with("/etc/ld.so.cache") { + if src.contains("/var/lib/pacman") || src.ends_with("/etc/ld.so.cache") { continue; } let metadata = match entry.metadata() { - Ok(meta) => meta, Err(_) => continue + Ok(meta) => meta, + Err(_) => continue, }; state.files.insert(src_tr, (FileType::from(metadata), src)); @@ -408,31 +421,31 @@ fn obtain_state(root: Arc, state: &mut FileSystemState) { fn link_filesystem(state: &FileSystemState, root: &str) { state.files.par_iter().for_each(|file| { - if let FileType::SymLink = file.1.0 { - if let Err(error) = create_soft_link(&file.1.1, &format!("{}{}", root, file.0)) { + if let FileType::SymLink = file.1 .0 { + if let Err(error) = create_soft_link(&file.1 .1, &format!("{}{}", root, file.0)) { print_warning(error); } - } else if let FileType::HardLink = file.1.0 { - if let Err(error) = create_hard_link(&file.1.1, &format!("{}{}", root, file.0)) { + } else if let FileType::HardLink = file.1 .0 { + if let Err(error) = create_hard_link(&file.1 .1, &format!("{}{}", root, file.0)) { print_warning(error); } } }); } - -fn delete_files(state: &FileSystemState, state_res: &FileSystemState, root: &str) { + +fn delete_files(state: &FileSystemState, state_res: &FileSystemState, root: &str) { let (tx, rx) = mpsc::sync_channel(0); let tx_clone: mpsc::SyncSender<()> = tx.clone(); - state_res.files.par_iter().for_each(|file| { - let _ = tx_clone; - + state_res.files.par_iter().for_each(|file| { + let _ = tx_clone; + if let None = state.files.get(file.0) { let path: &str = &format!("{}{}", root, file.0); - let path = Path::new(path); + let path = Path::new(path); - if ! path.exists() { - if let FileType::SymLink = file.1.0 { + if !path.exists() { + if let FileType::SymLink = file.1 .0 { if let Err(error) = remove_symlink(path) { print_warning(error); } @@ -440,34 +453,34 @@ fn delete_files(state: &FileSystemState, state_res: &FileSystemState, root: &str return; } - if let FileType::HardLink = file.1.0 { - if let Err(error) = remove_file(path) { - print_warning(error); + if let FileType::HardLink = file.1 .0 { + if let Err(error) = remove_file(path) { + print_warning(error); } } - } + } }); drop(tx); rx.try_iter(); } -fn delete_directories(state: &FileSystemState, state_res: &FileSystemState, root: &str) { +fn delete_directories(state: &FileSystemState, state_res: &FileSystemState, root: &str) { let (tx, rx) = mpsc::sync_channel(0); let tx_clone: mpsc::SyncSender<()> = tx.clone(); - state_res.files.par_iter().for_each(move |file| { + state_res.files.par_iter().for_each(move |file| { let _ = tx_clone; - + if let None = state.files.get(file.0) { let path: &str = &format!("{}{}", root, file.0); - let path = Path::new(path); - - if ! path.exists() { + let path = Path::new(path); + + if !path.exists() { return; } - - if let FileType::Directory = file.1.0 { + + if let FileType::Directory = file.1 .0 { remove_directory(path).ok(); } } @@ -477,7 +490,7 @@ fn delete_directories(state: &FileSystemState, state_res: &FileSystemState, root rx.try_iter(); } -fn create_soft_link(src: &str, dest: &str) -> Result<(),String> { +fn create_soft_link(src: &str, dest: &str) -> Result<(), String> { let dest_path = Path::new(&dest); let src_path = match fs::read_link(src) { Ok(path) => path, @@ -495,11 +508,11 @@ fn create_soft_link(src: &str, dest: &str) -> Result<(),String> { } else if dest_path.exists() { remove_file(dest_path) } else { - remove_symlink(dest_path) + remove_symlink(dest_path) }?; if let Some(path) = dest_path.parent() { - if ! path.exists() { + if !path.exists() { create_directory(&path)?; } } @@ -507,19 +520,19 @@ fn create_soft_link(src: &str, dest: &str) -> Result<(),String> { soft_link(&src_path, dest_path) } -pub fn create_hard_link(src: &str, dest: &str) -> Result<(), String> { - let src_path = Path::new(&src); - let dest_path = Path::new(&dest); +pub fn create_hard_link(src: &str, dest: &str) -> Result<(), String> { + let src_path = Path::new(&src); + let dest_path = Path::new(&dest); - if ! src_path.exists() { + if !src_path.exists() { Err(format!("Source file '{}': entity not found.", &src))? } - if ! dest_path.exists() { + if !dest_path.exists() { if let Some(path) = dest_path.parent() { - if ! path.exists() { + if !path.exists() { remove_symlink(&path)?; - create_directory(&path)?; + create_directory(&path)?; } } @@ -527,7 +540,7 @@ pub fn create_hard_link(src: &str, dest: &str) -> Result<(), String> { hard_link(src_path, dest_path) } else { let meta_dest = metadata(&dest_path)?; - let meta_src = metadata(&src_path)?; + let meta_src = metadata(&src_path)?; if meta_src.ino() != meta_dest.ino() { if meta_dest.is_dir() { @@ -547,30 +560,30 @@ fn queue_status(queue: &HashSet<&str>, compare: &str, max_chars: usize) -> Strin let mut char_amt = 0; let mut diff = 0; let mut string = String::new(); - let mut strs: Vec<&str> = Vec::new(); + let mut strs: Vec<&str> = Vec::new(); - for contrast in queue { + for contrast in queue { let contrast: &str = contrast.as_ref(); if compare == contrast { continue; } - char_amt += contrast.len(); + char_amt += contrast.len(); if char_amt >= max_chars - contrast.len() { - diff = queue.len()-strs.len(); + diff = queue.len() - strs.len(); break; } strs.push(contrast); } - for idx in 0..strs.len() { + for idx in 0 .. strs.len() { let str = strs.get(idx).unwrap(); if idx > 0 { - string.push_str(format!(", {str}").as_str()); + string.push_str(format!(", {str}").as_str()); } else { string.push_str(format!("{str}").as_str()); } @@ -587,24 +600,24 @@ fn queue_status(queue: &HashSet<&str>, compare: &str, max_chars: usize) -> Strin string } -fn metadata(path: &Path) -> Result { +fn metadata(path: &Path) -> Result { match fs::metadata(path) { Ok(meta) => Ok(meta), Err(err) => Err(format!("Failed to obtain metadata for '{}': {}", path.to_str().unwrap(), err.kind())), } } -fn hard_link(src_path: &Path, dest_path: &Path) -> Result<(),String> { - if let Err(err) = fs::hard_link(src_path,dest_path) { - Err(format!("Failed to link '{}': {}", dest_path.to_str().unwrap(), err.kind()))? +fn hard_link(src_path: &Path, dest_path: &Path) -> Result<(), String> { + if let Err(err) = fs::hard_link(src_path, dest_path) { + Err(format!("Failed to link '{}': {}", dest_path.to_str().unwrap(), err.kind()))? } Ok(()) } -fn soft_link<'a>(src_path: &'a Path, dest_path: &'a Path) -> Result<(),String> { +fn soft_link<'a>(src_path: &'a Path, dest_path: &'a Path) -> Result<(), String> { if let Err(err) = symlink(src_path, dest_path) { - Err(format!("Failed to create symlink '{}': {}", dest_path.to_str().unwrap(), err.kind()))? + Err(format!("Failed to create symlink '{}': {}", dest_path.to_str().unwrap(), err.kind()))? } Ok(()) @@ -616,7 +629,7 @@ fn create_directory(path: &Path) -> Result<(), String> { } Ok(()) -} +} fn remove_directory(path: &Path) -> Result<(), String> { if let Err(err) = fs::remove_dir_all(path) { @@ -624,21 +637,21 @@ fn remove_directory(path: &Path) -> Result<(), String> { } Ok(()) -} +} fn remove_file(path: &Path) -> Result<(), String> { - if let Err(err) = fs::remove_file(path) { + if let Err(err) = fs::remove_file(path) { Err(format!("Failed to remove file '{}': {}", path.to_str().unwrap(), err.kind()))? } Ok(()) } -fn remove_symlink(path: &Path) -> Result<(),String> { +fn remove_symlink(path: &Path) -> Result<(), String> { if let Ok(_) = fs::read_link(path) { - if let Err(err) = fs::remove_file(path) { + if let Err(err) = fs::remove_file(path) { Err(format!("Failed to delete symlink '{}': {}", path.to_str().unwrap(), err.kind()))? - } + } } Ok(()) diff --git a/pacwrap-core/src/sync/resolver.rs b/pacwrap-core/src/sync/resolver.rs index e61f15e..dac573b 100644 --- a/pacwrap-core/src/sync/resolver.rs +++ b/pacwrap-core/src/sync/resolver.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,9 +19,13 @@ use std::collections::HashSet; -use alpm::{Package, Alpm}; +use alpm::{Alpm, Package}; -use crate::{err, Error, sync::{SyncError, utils::AlpmUtils}}; +use crate::{ + err, + sync::{utils::AlpmUtils, SyncError}, + Error, +}; pub struct DependencyResolver<'a> { resolved: HashSet<&'a str>, @@ -30,9 +34,9 @@ pub struct DependencyResolver<'a> { ignored: &'a HashSet, handle: &'a Alpm, depth: isize, -} +} -impl <'a>DependencyResolver<'a> { +impl<'a> DependencyResolver<'a> { pub fn new(alpm: &'a Alpm, ignorelist: &'a HashSet) -> Self { Self { resolved: HashSet::new(), @@ -45,17 +49,17 @@ impl <'a>DependencyResolver<'a> { } fn check_depth(&mut self) -> Result<(), Error> { - if self.depth == 50 { + if self.depth == 50 { err!(SyncError::RecursionDepthExceeded(self.depth))? } self.depth += 1; Ok(()) } - + pub fn enumerate(mut self, packages: &Vec<&'a str>) -> Result<(Option>, Vec>), Error> { - let mut synchronize: Vec<&'a str> = Vec::new(); - + let mut synchronize: Vec<&'a str> = Vec::new(); + for pkg in packages { if let Some(_) = self.resolved.get(pkg) { continue; @@ -65,28 +69,29 @@ impl <'a>DependencyResolver<'a> { continue; } - if let Some(pkg) = self.handle.get_package(pkg) { + if let Some(pkg) = self.handle.get_package(pkg) { self.packages.push(pkg); self.resolved.insert(pkg.name()); - synchronize.extend(pkg.depends() - .iter() - .filter_map(|p| - match self.handle.get_local_package(p.name()) { - None => match self.handle.get_package(p.name()) { - Some(dep) => Some(dep.name()), None => None, + synchronize.extend( + pkg.depends() + .iter() + .filter_map(|p| match self.handle.get_local_package(p.name()) { + None => match self.handle.get_package(p.name()) { + Some(dep) => Some(dep.name()), + None => None, }, Some(_) => None, - } - ) - .collect::>()); + }) + .collect::>(), + ); if self.depth > 0 { self.keys.push(pkg.name().into()); } - } + } } - if synchronize.len() > 0 { + if synchronize.len() > 0 { self.check_depth()?; self.enumerate(&synchronize) } else { diff --git a/pacwrap-core/src/sync/resolver_local.rs b/pacwrap-core/src/sync/resolver_local.rs index a2833dc..c478e48 100644 --- a/pacwrap-core/src/sync/resolver_local.rs +++ b/pacwrap-core/src/sync/resolver_local.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,9 +19,13 @@ use std::collections::HashSet; -use alpm::{Package, Alpm, PackageReason}; +use alpm::{Alpm, Package, PackageReason}; -use crate::{err, Error, sync::{SyncError, transaction::TransactionType, utils::AlpmUtils}}; +use crate::{ + err, + sync::{transaction::TransactionType, utils::AlpmUtils, SyncError}, + Error, +}; pub struct LocalDependencyResolver<'a> { resolved: HashSet<&'a str>, @@ -30,9 +34,9 @@ pub struct LocalDependencyResolver<'a> { handle: &'a Alpm, depth: isize, flags: (bool, bool, bool), -} +} -impl <'a>LocalDependencyResolver<'a> { +impl<'a> LocalDependencyResolver<'a> { pub fn new(alpm: &'a Alpm, ignorelist: &'a HashSet, trans_type: &TransactionType) -> Self { Self { resolved: HashSet::new(), @@ -51,14 +55,14 @@ impl <'a>LocalDependencyResolver<'a> { if self.depth == 50 { err!(SyncError::RecursionDepthExceeded(self.depth))? } - + self.depth += 1; Ok(()) } - + pub fn enumerate(mut self, packages: &Vec<&'a str>) -> Result>, Error> { let mut synchronize: Vec<&'a str> = Vec::new(); - + for pkg in packages { if let Some(_) = self.resolved.get(pkg) { continue; @@ -68,50 +72,46 @@ impl <'a>LocalDependencyResolver<'a> { continue; } - if let Some(pkg) = self.handle.get_local_package(pkg) { + if let Some(pkg) = self.handle.get_local_package(pkg) { if self.depth > 0 { //TODO: Implement proper explicit package handling - if ! self.flags.1 - && pkg.reason() == PackageReason::Explicit { + if !self.flags.1 && pkg.reason() == PackageReason::Explicit { continue; } - - if pkg.required_by() + + if pkg + .required_by() .iter() - .filter_map(|p| - match self.resolved.get(p) { - None => Some(()), Some(_) => None + .filter_map(|p| match self.resolved.get(p) { + None => Some(()), + Some(_) => None, }) - .count() > 0 { + .count() + > 0 + { continue; } } self.packages.push(pkg); self.resolved.insert(pkg.name()); - - if ! self.flags.0 { + + if !self.flags.0 { continue; } - synchronize.extend(pkg.depends() - .iter() - .map(|pkg| pkg.name()) - .collect::>()); + synchronize.extend(pkg.depends().iter().map(|pkg| pkg.name()).collect::>()); - if ! self.flags.1 { + if !self.flags.1 { continue; } - for package in self.handle.localdb().pkgs() { - if package.depends() - .iter() - .filter_map(|d| self.resolved.get(d.name())) - .count() > 0 { + for package in self.handle.localdb().pkgs() { + if package.depends().iter().filter_map(|d| self.resolved.get(d.name())).count() > 0 { synchronize.push(package.name()); } } - } + } } if synchronize.len() > 0 && self.flags.0 { diff --git a/pacwrap-core/src/sync/transaction.rs b/pacwrap-core/src/sync/transaction.rs index fff18f3..9fcaebf 100644 --- a/pacwrap-core/src/sync/transaction.rs +++ b/pacwrap-core/src/sync/transaction.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,31 +19,32 @@ use std::{borrow::Cow, collections::HashSet}; -use bitflags::bitflags; use alpm::{Alpm, PackageReason, TransFlag}; +use bitflags::bitflags; use serde::{Deserialize, Serialize}; -use crate::{err, - Error, - config::{InstanceHandle, Global}, - constants::{RESET, BOLD, ARROW_CYAN, BAR_CYAN, BOLD_YELLOW, BOLD_GREEN}, - sync::{SyncError, - transaction::{stage::Stage, - commit::Commit, - prepare::Prepare, - uptodate::UpToDate}, - resolver_local::LocalDependencyResolver, +use crate::{ + config::{Global, InstanceHandle}, + constants::{ARROW_CYAN, BAR_CYAN, BOLD, BOLD_GREEN, BOLD_YELLOW, RESET}, + err, + sync::{ resolver::DependencyResolver, - utils::AlpmUtils}, - utils::print_warning}; + resolver_local::LocalDependencyResolver, + transaction::{commit::Commit, prepare::Prepare, stage::Stage, uptodate::UpToDate}, + utils::AlpmUtils, + SyncError, + }, + utils::print_warning, + Error, +}; pub use self::aggregator::TransactionAggregator; pub mod aggregator; mod commit; mod prepare; -mod uptodate; mod stage; +mod uptodate; pub type Result = crate::Result; pub static MAGIC_NUMBER: u32 = 663445956; @@ -52,7 +53,7 @@ pub enum TransactionState { Complete(bool), Prepare, UpToDate, - PrepareForeign, + PrepareForeign(bool), Stage, StageForeign, Commit(bool), @@ -68,7 +69,7 @@ pub enum TransactionType { #[derive(Serialize, Deserialize, Copy, Clone)] pub enum TransactionMode { Foreign, - Local + Local, } pub enum SyncReqResult { @@ -77,14 +78,21 @@ pub enum SyncReqResult { } pub trait Transaction { - fn new(new: TransactionState, ag: &TransactionAggregator) -> Box where Self: Sized; - fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result; + fn new(new: TransactionState, ag: &TransactionAggregator) -> Box + where + Self: Sized; + fn engage( + &self, + ag: &mut TransactionAggregator, + handle: &mut TransactionHandle, + inshandle: &InstanceHandle, + ) -> Result; } bitflags! { pub struct TransactionFlags: u8 { const NONE = 0; - const TARGET_ONLY = 0b0000001; + const TARGET_ONLY = 0b0000001; const PREVIEW = 0b0000010; const NO_CONFIRM = 0b0000100; const FORCE_DATABASE = 0b0001000; @@ -104,11 +112,11 @@ pub struct TransactionHandle<'a> { #[derive(Serialize, Deserialize, Clone)] pub struct TransactionMetadata<'a> { - foreign_pkgs: HashSet, + foreign_pkgs: HashSet, resident_pkgs: HashSet, queue: Vec>, mode: TransactionMode, - flags: (u8, u32) + flags: (u8, u32), } #[derive(Serialize, Deserialize)] @@ -120,8 +128,7 @@ pub struct TransactionParameters { bytes: u64, files: u64, action: TransactionType, - mode: TransactionMode, - + mode: TransactionMode, } impl TransactionMode { @@ -137,10 +144,10 @@ impl TransactionState { fn from(self, ag: &TransactionAggregator) -> Box { match self { Self::Prepare => Prepare::new(self, ag), - Self::PrepareForeign => Prepare::new(self, ag), + Self::PrepareForeign(_) => Prepare::new(self, ag), Self::UpToDate => UpToDate::new(self, ag), Self::Stage => Stage::new(self, ag), - Self::StageForeign => Stage::new(self, ag), + Self::StageForeign => Stage::new(self, ag), Self::Commit(_) => Commit::new(self, ag), Self::CommitForeign => Commit::new(self, ag), Self::Complete(_) => unreachable!(), @@ -151,23 +158,23 @@ impl TransactionState { match self { Self::Commit(_) => "resident", Self::CommitForeign => "foreign", - _ => "" + _ => "", } } } -impl TransactionType { +impl TransactionType { pub fn pr_offset(&self) -> usize { match self { Self::Upgrade(..) => 1, - Self::Remove(..) => 0 + Self::Remove(..) => 0, } } fn as_str(&self) -> &str { match self { Self::Upgrade(..) => "installation", - Self::Remove(..) => "removal" + Self::Remove(..) => "removal", } } @@ -175,11 +182,11 @@ impl TransactionType { let message = match self { Self::Upgrade(..) => match state { TransactionMode::Foreign => "Synchronizing foreign database...", - TransactionMode::Local => "Synchronizing resident container..." - }, + TransactionMode::Local => "Synchronizing resident container...", + }, Self::Remove(..) => match state { TransactionMode::Foreign => "Preparing foreign package removal...", - TransactionMode::Local => "Preparing package removal..." + TransactionMode::Local => "Preparing package removal...", }, }; @@ -189,95 +196,96 @@ impl TransactionType { fn begin_message(&self, inshandle: &InstanceHandle) { let instance = inshandle.vars().instance(); let message = match self { - Self::Upgrade(upgrade,..) => match upgrade { + Self::Upgrade(upgrade, ..) => match upgrade { true => format!("Checking {instance} for updates..."), - false => format!("Transacting {instance}...") - } - Self::Remove(..) => format!("Transacting {instance}...") + false => format!("Transacting {instance}..."), + }, + Self::Remove(..) => format!("Transacting {instance}..."), }; println!("{} {}{message}{}", *BAR_CYAN, *BOLD, *RESET); } } -impl <'a>TransactionMetadata<'a> { +impl<'a> TransactionMetadata<'a> { fn new(queue: Vec<&'a str>) -> TransactionMetadata { - Self { + Self { foreign_pkgs: HashSet::new(), - resident_pkgs: HashSet::new(), + resident_pkgs: HashSet::new(), mode: TransactionMode::Local, queue: queue.iter().map(|q| (*q).into()).collect::>(), flags: (0, 0), } - } + } } -impl <'a>TransactionHandle<'a> { +impl<'a> TransactionHandle<'a> { pub fn new(global: &'a Global, alpm_handle: Alpm, metadata: &'a mut TransactionMetadata<'a>) -> Self { Self { meta: metadata, alpm: Some(alpm_handle), - deps: None, + deps: None, fail: true, config: global, - } + } } fn is_sync_req(&self, mode: TransactionMode) -> SyncReqResult { let alpm = self.alpm(); - let ignored = match mode { + let ignored = match mode { TransactionMode::Foreign => &self.meta.resident_pkgs, TransactionMode::Local => &self.meta.foreign_pkgs, }; - for pkg in alpm.localdb().pkgs() { + for pkg in alpm.localdb().pkgs() { if let Some(_) = ignored.get(pkg.name()) { continue; } - if let Some(_) = pkg.sync_new_version(alpm.syncdbs()) { - return SyncReqResult::Required - } + if let Some(_) = pkg.sync_new_version(alpm.syncdbs()) { + return SyncReqResult::Required; + } } SyncReqResult::NotRequired } fn enumerate_package_lists(&mut self, dep_handle: &Alpm, queue: bool) { - let foreign_pkgs = dep_handle.localdb() - .pkgs() - .iter() - .filter(|p| ! self.meta.foreign_pkgs.contains(p.name())) - .map(|p| p.name().to_owned()) - .collect::>(); - let resident_pkgs = self.alpm() + let foreign_pkgs = dep_handle .localdb() .pkgs() .iter() - .filter(|p| ! self.meta.foreign_pkgs.contains(p.name()) - && ! self.meta.resident_pkgs.contains(p.name())) + .filter(|p| !self.meta.foreign_pkgs.contains(p.name())) + .map(|p| p.name().to_owned()) + .collect::>(); + let resident_pkgs = self + .alpm() + .localdb() + .pkgs() + .iter() + .filter(|p| !self.meta.foreign_pkgs.contains(p.name()) && !self.meta.resident_pkgs.contains(p.name())) .map(|a| a.name().to_owned()) .collect::>(); - if queue { - self.meta.queue.extend(foreign_pkgs.iter() - .map(|p| p.to_owned().into()) - .collect::>()); + if queue { + let to_queue = foreign_pkgs.iter().map(|p| p.to_owned().into()).collect::>(); + + self.meta.queue.extend(to_queue); self.fail = false; } - self.meta.foreign_pkgs.extend(foreign_pkgs); + self.meta.foreign_pkgs.extend(foreign_pkgs); self.meta.resident_pkgs.extend(resident_pkgs); - } + } pub fn ignore(&mut self, silent: bool) { - let mut fail = self.fail; + let mut fail = self.fail; let alpm = self.alpm.as_mut().unwrap(); - let ignore = match self.meta.mode { + let ignore = match self.meta.mode { TransactionMode::Foreign => &self.meta.resident_pkgs, TransactionMode::Local => &self.meta.foreign_pkgs, }; - let unignore = match self.meta.mode { + let unignore = match self.meta.mode { TransactionMode::Local => &self.meta.resident_pkgs, TransactionMode::Foreign => &self.meta.foreign_pkgs, }; @@ -298,10 +306,14 @@ impl <'a>TransactionHandle<'a> { .localdb() .pkgs() .iter() - .filter(|a| ! ignore.contains(a.name()) - && self.config.alpm().ignored().contains(&a.name())) { - let new = match package.sync_new_version(alpm.syncdbs()) { - Some(new) => { fail = false; new }, None => continue, + .filter(|a| !ignore.contains(a.name()) && self.config.alpm().ignored().contains(&a.name())) + { + let new = match package.sync_new_version(alpm.syncdbs()) { + Some(new) => { + fail = false; + new + } + None => continue, }; if silent { @@ -309,16 +321,13 @@ impl <'a>TransactionHandle<'a> { } let name = package.name(); - let ver = package.version(); + let ver = package.version(); let ver_new = new.version(); - print_warning(format!("{}{name}{}: Ignoring package upgrade ({}{ver}{} => {}{ver_new}{})", - *BOLD, - *RESET, - *BOLD_YELLOW, - *RESET, - *BOLD_GREEN, - *RESET)); + print_warning(format!( + "{}{name}{}: Ignoring package upgrade ({}{ver}{} => {}{ver_new}{})", + *BOLD, *RESET, *BOLD_YELLOW, *RESET, *BOLD_GREEN, *RESET + )); } self.fail = fail; @@ -326,49 +335,40 @@ impl <'a>TransactionHandle<'a> { pub fn prepare(&mut self, trans_type: &TransactionType, flags: &TransactionFlags) -> Result<()> { let alpm = self.alpm.as_mut().unwrap(); - let ignored = match self.meta.mode { + let ignored = match self.meta.mode { TransactionMode::Foreign => &self.meta.resident_pkgs, TransactionMode::Local => &self.meta.foreign_pkgs, }; - let queue = self.meta.queue.iter() - .map(|i| i.as_ref()) - .collect::>(); + let queue = self.meta.queue.iter().map(|i| i.as_ref()).collect::>(); if let TransactionMode::Local = self.meta.mode { - let upstream = queue.iter() - .map(|a| *a) - .filter(|a| ignored.contains(*a)) - .collect::>(); + let upstream = queue.iter().map(|a| *a).filter(|a| ignored.contains(*a)).collect::>(); - if ! flags.contains(TransactionFlags::FORCE_DATABASE) - && ! upstream.is_empty() { - err!(SyncError::TargetUpstream(upstream[0].into()))? + if !flags.contains(TransactionFlags::FORCE_DATABASE) && !upstream.is_empty() { + err!(SyncError::TargetUpstream(upstream[0].into()))? } } - + match trans_type { - TransactionType::Remove(..) => { - let not_installed = queue.iter() - .map(|a| *a) - .filter(|a| ! ignored.contains(*a) - && alpm.get_local_package(a).is_none()) + TransactionType::Remove(..) => { + let not_installed = queue + .iter() + .map(|a| *a) + .filter(|a| !ignored.contains(*a) && alpm.get_local_package(a).is_none()) .collect::>(); - if ! not_installed.is_empty() { + if !not_installed.is_empty() { err!(SyncError::TargetNotInstalled(not_installed[0].into()))? } - for pkg in LocalDependencyResolver::new(alpm, &ignored, trans_type).enumerate(&queue)? { - alpm.trans_remove_pkg(pkg).unwrap(); + for pkg in LocalDependencyResolver::new(alpm, &ignored, trans_type).enumerate(&queue)? { + alpm.trans_remove_pkg(pkg).unwrap(); } - }, - TransactionType::Upgrade(..) => { - let not_available = queue.iter() - .map(|a| *a) - .filter(|a| alpm.get_package(a).is_none()) - .collect::>(); + } + TransactionType::Upgrade(..) => { + let not_available = queue.iter().map(|a| *a).filter(|a| alpm.get_package(a).is_none()).collect::>(); - if ! not_available.is_empty() { + if !not_available.is_empty() { err!(SyncError::TargetNotAvailable(not_available[0].into()))? } @@ -379,7 +379,7 @@ impl <'a>TransactionHandle<'a> { continue; } - alpm.trans_add_pkg(pkg).unwrap(); + alpm.trans_add_pkg(pkg).unwrap(); } self.deps = packages.0; @@ -392,36 +392,40 @@ impl <'a>TransactionHandle<'a> { fn apply_configuration(&mut self, instance: &InstanceHandle, create: bool) -> Result<()> { let depends = instance.metadata().dependencies(); let explicit_packages: Vec<&str> = instance.metadata().explicit_packages(); - let pkgs = self.alpm + let pkgs = self + .alpm .as_mut() .unwrap() .localdb() .pkgs() .iter() - .filter(|p| p.reason() == PackageReason::Explicit - && ! p.name().starts_with("pacwrap-") - && ! self.meta.foreign_pkgs.contains(p.name())) + .filter(|p| { + p.reason() == PackageReason::Explicit + && !p.name().starts_with("pacwrap-") + && !self.meta.foreign_pkgs.contains(p.name()) + }) .map(|p| p.name()) - .collect(); + .collect(); if pkgs != explicit_packages || create { let mut instance = instance.clone(); instance.metadata_mut().set(depends, pkgs); - instance.save()?; + instance.save()?; drop(instance); } Ok(()) } - pub fn trans_ready(&mut self, trans_type: &TransactionType) -> Result<()> { + pub fn trans_ready(&mut self, trans_type: &TransactionType) -> Result<()> { if match trans_type { TransactionType::Upgrade(..) => self.alpm().trans_add().len(), - TransactionType::Remove(..) => self.alpm().trans_remove().len() - } > 0 { + TransactionType::Remove(..) => self.alpm().trans_remove().len(), + } > 0 + { Ok(()) - } else { + } else { err!(SyncError::NothingToDo(self.fail)) } } @@ -437,20 +441,20 @@ impl <'a>TransactionHandle<'a> { pub fn release(self) { drop(self); } - - fn set_mode(&mut self, modeset: TransactionMode) { - self.meta.mode = modeset; + + fn set_mode(&mut self, modeset: TransactionMode) { + self.meta.mode = modeset; } - pub fn get_mode(&self) -> &TransactionMode { - &self.meta.mode + pub fn get_mode(&self) -> &TransactionMode { + &self.meta.mode } - - pub fn alpm_mut(&mut self) -> &mut Alpm { + + pub fn alpm_mut(&mut self) -> &mut Alpm { self.alpm.as_mut().unwrap() } - - pub fn alpm(&self) -> &Alpm { + + pub fn alpm(&self) -> &Alpm { self.alpm.as_ref().unwrap() } @@ -459,7 +463,7 @@ impl <'a>TransactionHandle<'a> { } pub fn set_flags(&mut self, flags: &TransactionFlags, flags_alpm: TransFlag) { - self.meta.flags = (flags.bits(), flags_alpm.bits()); + self.meta.flags = (flags.bits(), flags_alpm.bits()); } pub fn retrieve_flags(&self) -> (Option, Option) { diff --git a/pacwrap-core/src/sync/transaction/aggregator.rs b/pacwrap-core/src/sync/transaction/aggregator.rs index cbdbc9c..54d3b33 100644 --- a/pacwrap-core/src/sync/transaction/aggregator.rs +++ b/pacwrap-core/src/sync/transaction/aggregator.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,23 +19,21 @@ use std::collections::HashMap; -use crate::{err, - ErrorKind, - error::*, +use crate::{ + config::{cache::InstanceCache, InstanceHandle, InstanceType, CONFIG}, constants::{ARROW_GREEN, UNIX_TIMESTAMP}, - config::InstanceType, - exec::utils::execute_fakeroot_container, - log::Logger, - sync::{self, - SyncError, - filesystem::FileSystemStateSync, - transaction::{Transaction, - TransactionHandle, - TransactionState, - TransactionType, - TransactionFlags, - TransactionMetadata}}, - config::{InstanceHandle, InstanceType::ROOT,cache::InstanceCache, CONFIG}}; + err, + error::*, + exec::utils::execute_fakeroot_container, + log::Logger, + sync::{ + self, + filesystem::FileSystemStateSync, + transaction::{Transaction, TransactionFlags, TransactionHandle, TransactionMetadata, TransactionState, TransactionType}, + SyncError, + }, + ErrorKind, +}; pub struct TransactionAggregator<'a> { queried: Vec<&'a str>, @@ -50,23 +48,25 @@ pub struct TransactionAggregator<'a> { target: Option<&'a str>, } -impl <'a>TransactionAggregator<'a> { - pub fn new(inscache: &'a InstanceCache, - queue: HashMap<&'a str, Vec<&'a str>>, - log: &'a mut Logger, - action_flags: TransactionFlags, - action_type: TransactionType, - current_target: Option<&'a str>) -> Self { +impl<'a> TransactionAggregator<'a> { + pub fn new( + inscache: &'a InstanceCache, + queue: HashMap<&'a str, Vec<&'a str>>, + log: &'a mut Logger, + action_flags: TransactionFlags, + action_type: TransactionType, + current_target: Option<&'a str>, + ) -> Self { Self { queried: Vec::new(), updated: Vec::new(), pkg_queue: queue, filesystem_state: Some(FileSystemStateSync::new(inscache)), - action: action_type, + action: action_type, cache: inscache, keyring: false, logger: log, - flags: action_flags, + flags: action_flags, target: current_target, } } @@ -76,46 +76,48 @@ impl <'a>TransactionAggregator<'a> { let upgrade = match self.action { TransactionType::Upgrade(upgrade, refresh, force) => { if refresh { - sync::synchronize_database(self.cache, force)?; + sync::synchronize_database(self.cache, force)?; } upgrade - }, + } _ => false, }; let target = match self.target { - Some(s) => self.cache.get_instance_option(s), None => None + Some(s) => self.cache.get_instance_option(s), + None => None, }; + let downstream = self.cache.filter(vec![InstanceType::Aggregate]); + let upstream = self.cache.filter(vec![InstanceType::Base, InstanceType::Slice]); + let containers = (upstream, downstream); - if let Some(inshandle) = target { - if let InstanceType::BASE | InstanceType::DEP = inshandle.metadata().container_type() { - self.transact(inshandle)?; + if let Some(ins) = target { + if let InstanceType::Base | InstanceType::Slice = ins.metadata().container_type() { + self.transact(ins)?; } } else if upgrade { - self.transaction(self.cache.registered_base())?; - self.transaction(self.cache.registered_dep())?; + self.transaction(&containers.0)?; } - if self.flags.intersects(TransactionFlags::FILESYSTEM_SYNC | TransactionFlags::CREATE) - || self.updated.len() > 0 { - if self.cache.registered_root().len() > 0 { + if self.flags.intersects(TransactionFlags::FILESYSTEM_SYNC | TransactionFlags::CREATE) || self.updated.len() > 0 { + if containers.1.len() > 0 { let file = self.cache.registered(); let linker = self.fs_sync().unwrap(); linker.prepare(file.len()); - linker.engage(file)?; + linker.engage(&file)?; linker.finish(); } - - self.filesystem_state = self.filesystem_state.unwrap().release(); + + self.filesystem_state = self.filesystem_state.unwrap().release(); } - if let Some(inshandle) = target { - if let InstanceType::ROOT = inshandle.metadata().container_type() { - self.transact(inshandle)?; + if let Some(ins) = target { + if let InstanceType::Aggregate = ins.metadata().container_type() { + self.transact(ins)?; } } else if upgrade { - self.transaction(self.cache.registered_root())?; + self.transaction(&containers.1)?; } println!("{} Transaction complete.", *ARROW_GREEN); @@ -123,12 +125,15 @@ impl <'a>TransactionAggregator<'a> { } pub fn transaction(&mut self, containers: &Vec<&'a str>) -> Result<()> { - for ins in containers.iter() { + for ins in containers.iter() { if self.queried.contains(ins) { continue; } - let inshandle = self.cache.get_instance(ins).unwrap(); + let inshandle = match self.cache.get_instance_option(ins) { + Some(ins) => ins, + None => continue, + }; self.queried.push(ins); self.transaction(&inshandle.metadata().dependencies())?; @@ -138,15 +143,16 @@ impl <'a>TransactionAggregator<'a> { Ok(()) } - pub fn transact(&mut self, inshandle: &'a InstanceHandle) -> Result<()> { + pub fn transact(&mut self, inshandle: &'a InstanceHandle) -> Result<()> { let queue = match self.pkg_queue.get(inshandle.vars().instance()) { - Some(some) => some.clone(), None => Vec::new(), + Some(some) => some.clone(), + None => Vec::new(), }; let alpm = sync::instantiate_alpm(&inshandle); let mut meta = TransactionMetadata::new(queue); let mut handle = TransactionHandle::new(&*CONFIG, alpm, &mut meta); let mut act: Box = TransactionState::Prepare.from(self); - + self.action.begin_message(&inshandle); loop { @@ -158,15 +164,16 @@ impl <'a>TransactionAggregator<'a> { } handle.release(); - return Ok(()) + return Ok(()); } - + result - }, + } Err(result) => { - let is_okay = match result.downcast::() { - Ok(error) => match error { - SyncError::NothingToDo(bool) => *bool, _ => true, + let is_okay = match result.downcast::() { + Ok(error) => match error { + SyncError::NothingToDo(bool) => *bool, + _ => true, }, Err(_) => true, }; @@ -177,22 +184,22 @@ impl <'a>TransactionAggregator<'a> { false => return Ok(()), true => return Err(result), } - } + } }; - + act = result.from(self); } } pub fn keyring_update(&mut self, inshandle: &InstanceHandle) -> Result<()> { - execute_fakeroot_container(inshandle, vec!("/usr/bin/pacman-key", "--populate", "archlinux"))?; - execute_fakeroot_container(inshandle, vec!("/usr/bin/pacman-key", "--updatedb"))?; + execute_fakeroot_container(inshandle, vec!["/usr/bin/pacman-key", "--populate", "archlinux"])?; + execute_fakeroot_container(inshandle, vec!["/usr/bin/pacman-key", "--updatedb"])?; self.keyring = true; Ok(()) } - pub fn sync_filesystem(&mut self, inshandle: &'a InstanceHandle) -> Result<()> { - if let ROOT = inshandle.metadata().container_type() { + pub fn sync_filesystem(&mut self, inshandle: &'a InstanceHandle) -> Result<()> { + if let InstanceType::Aggregate = inshandle.metadata().container_type() { return Ok(()); } @@ -202,28 +209,28 @@ impl <'a>TransactionAggregator<'a> { fs_sync.engage(&vec![inshandle.vars().instance()]) } - pub fn cache(&self) -> &InstanceCache { + pub fn cache(&self) -> &InstanceCache { &self.cache } - - pub fn action(&self) -> &TransactionType { - &self.action + + pub fn action(&self) -> &TransactionType { + &self.action } - pub fn deps_updated(&self, inshandle: &InstanceHandle<'a>) -> bool { + pub fn deps_updated(&self, inshandle: &InstanceHandle<'a>) -> bool { for ins in inshandle.metadata().dependencies() { if self.updated.contains(&ins) { - return true + return true; } } false } - pub fn is_keyring_synced(&self) -> bool { - self.keyring + pub fn is_keyring_synced(&self) -> bool { + self.keyring } - + pub fn flags(&self) -> &TransactionFlags { &self.flags } @@ -232,7 +239,7 @@ impl <'a>TransactionAggregator<'a> { &mut self.logger } - pub fn fs_sync(&mut self) -> Result<&mut FileSystemStateSync<'a>> { + pub fn fs_sync(&mut self) -> Result<&mut FileSystemStateSync<'a>> { match self.filesystem_state.as_mut() { Some(linker) => Ok(linker), None => err!(ErrorKind::LinkerUninitialized), diff --git a/pacwrap-core/src/sync/transaction/commit.rs b/pacwrap-core/src/sync/transaction/commit.rs index 3dedd98..86aa879 100644 --- a/pacwrap-core/src/sync/transaction/commit.rs +++ b/pacwrap-core/src/sync/transaction/commit.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,57 +17,66 @@ * along with this program. If not, see . */ -use std::result::Result as StdResult; use alpm::Alpm; use dialoguer::console::Term; -use simplebyteunit::simplebyteunit::{SI, ToByteUnit}; +use simplebyteunit::simplebyteunit::{ToByteUnit, SI}; +use std::result::Result as StdResult; -use crate::{err, +use crate::{ + config::InstanceHandle, + constants::{BOLD, DIM, RESET}, + err, + exec::transaction_agent, + sync::{ + self, + transaction::{ + Transaction, + TransactionAggregator, + TransactionFlags, + TransactionHandle, + TransactionParameters, + TransactionState::{self, *}, + TransactionType::{self, *}, + }, + utils::erroneous_preparation, + SyncError, + }, + utils::prompt::prompt, Error, Result, - exec::transaction_agent, - sync::{self, - SyncError, - transaction::{Transaction, - TransactionState, - TransactionType, - TransactionHandle, - TransactionAggregator, - TransactionFlags, - TransactionParameters}, - utils::erroneous_preparation}, - utils::prompt::prompt, - constants::{RESET, BOLD, DIM}, - config::InstanceHandle}; - +}; pub struct Commit { state: TransactionState, keyring: bool, } -impl Transaction for Commit { +impl Transaction for Commit { fn new(new: TransactionState, _: &TransactionAggregator) -> Box { - let kr = match new { - TransactionState::Commit(bool) => bool, _ => false + let kr = match new { + Commit(bool) => bool, + _ => false, }; - Box::new(Self { - state: new, - keyring: kr, - }) + Box::new(Self { state: new, keyring: kr }) } - fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result { + fn engage( + &self, + ag: &mut TransactionAggregator, + handle: &mut TransactionHandle, + inshandle: &InstanceHandle, + ) -> Result { let instance = inshandle.vars().instance(); let ready = handle.trans_ready(&ag.action()); let state = self.state.as_str(); if let Err(_) = ready { - match ready_state(handle, ag.action(), &self.state) { - Some(result) => return result, None => ready?, - } - } + match ready_state(handle, ag.action(), &self.state) { + Some(result) => return result, + None => ready?, + } + } if let Err(error) = handle.alpm_mut().trans_prepare() { erroneous_preparation(error)? @@ -75,12 +84,13 @@ impl Transaction for Commit { let result = confirm(&self.state, ag, handle); let result = match result { - Err(result) => return result, Ok(result) => result + Err(result) => return result, + Ok(result) => result, }; - handle.set_alpm(None); + handle.set_alpm(None); - let parameters = &TransactionParameters::new(*ag.action(), *handle.get_mode(), result); + let parameters = &TransactionParameters::new(*ag.action(), *handle.get_mode(), result); let mut agent = transaction_agent(inshandle, parameters, handle.metadata())?; match agent.wait() { @@ -90,84 +100,100 @@ impl Transaction for Commit { ag.keyring_update(inshandle)?; } - handle.set_alpm(Some(sync::instantiate_alpm(inshandle))); - handle.apply_configuration(inshandle, ag.flags().intersects(TransactionFlags::CREATE))?; + handle.set_alpm(Some(sync::instantiate_alpm(inshandle))); + handle.apply_configuration(inshandle, ag.flags().intersects(TransactionFlags::CREATE))?; ag.logger().log(format!("container {instance}'s {state} transaction complete")).ok(); next_state(handle, ag.action(), &self.state, true) - }, + } 1 => err!(SyncError::TransactionFailureAgent), 2 => err!(SyncError::ParameterAcquisitionFailure), - 3 => err!(SyncError::DeserializationFailure), + 3 => err!(SyncError::DeserializationFailure), 4 => err!(SyncError::InvalidMagicNumber), 5 => err!(SyncError::AgentVersionMismatch), - _ => err!(SyncError::TransactionFailure(format!("Generic failure of agent: Exit code {}", exit_status.code().unwrap_or(-1))))?, + _ => err!(SyncError::TransactionFailure(format!( + "Generic failure of agent: Exit code {}", + exit_status.code().unwrap_or(-1) + )))?, }, Err(error) => err!(SyncError::TransactionFailure(format!("Execution of agent failed: {}", error)))?, } - } + } } -fn confirm(state: &TransactionState, ag: &TransactionAggregator, handle: &mut TransactionHandle) -> StdResult<(u64, u64), Result> { +fn confirm( + state: &TransactionState, + ag: &TransactionAggregator, + handle: &mut TransactionHandle, +) -> StdResult<(u64, u64), Result> { let sum = summary(handle.alpm()); - if ! handle.get_mode().bool() || ag.flags().intersects(TransactionFlags::DATABASE_ONLY | TransactionFlags::FORCE_DATABASE) { + if !handle.get_mode().bool() || ag.flags().intersects(TransactionFlags::DATABASE_ONLY | TransactionFlags::FORCE_DATABASE) { println!("{}", sum.0); if ag.flags().contains(TransactionFlags::PREVIEW) { - Err(next_state(handle, ag.action(), state, false))? - } + Err(next_state(handle, ag.action(), state, false))? + } - if ! ag.flags().contains(TransactionFlags::NO_CONFIRM) { + if !ag.flags().contains(TransactionFlags::NO_CONFIRM) { let action = ag.action().as_str(); let query = format!("Proceed with {action}?"); if let Err(_) = prompt("::", format!("{}{query}{}", *BOLD, *RESET), true) { Err(next_state(handle, ag.action(), state, false))? } - } + } } handle.alpm_mut().trans_release().ok(); Ok(sum.1) } -fn next_state<'a>(handle: &mut TransactionHandle, action: &TransactionType, state: &TransactionState, updated: bool) -> Result { +fn next_state<'a>( + handle: &mut TransactionHandle, + action: &TransactionType, + state: &TransactionState, + updated: bool, +) -> Result { handle.alpm_mut().trans_release().ok(); - + match action { - TransactionType::Remove(..) => Ok(match state { - TransactionState::CommitForeign => TransactionState::Complete(updated), - TransactionState::Commit(_) => TransactionState::PrepareForeign, - _ => unreachable!() + Remove(..) => Ok(match state { + CommitForeign => Complete(updated), + Commit(_) => PrepareForeign(updated), + _ => unreachable!(), }), - TransactionType::Upgrade(..) => Ok(match state { - TransactionState::Commit(_) => TransactionState::Complete(updated), - TransactionState::CommitForeign => TransactionState::Stage, - _ => unreachable!() + Upgrade(..) => Ok(match state { + Commit(_) => Complete(updated), + CommitForeign => Stage, + _ => unreachable!(), }), } } -fn ready_state<'a>(handle: &mut TransactionHandle, action: &TransactionType, state: &TransactionState) -> Option> { +fn ready_state<'a>( + handle: &mut TransactionHandle, + action: &TransactionType, + state: &TransactionState, +) -> Option> { handle.alpm_mut().trans_release().ok(); - - match action { - TransactionType::Remove(..) => match state { - TransactionState::CommitForeign => None, - TransactionState::Commit(_) => Some(Ok(TransactionState::PrepareForeign)), - _ => unreachable!() + + match action { + Remove(..) => match state { + CommitForeign => None, + Commit(_) => Some(Ok(PrepareForeign(false))), + _ => unreachable!(), }, - TransactionType::Upgrade(..) => match state { - TransactionState::Commit(_) => None, - TransactionState::CommitForeign => Some(Ok(TransactionState::Stage)), - _ => unreachable!() + Upgrade(..) => match state { + Commit(_) => None, + CommitForeign => Some(Ok(Stage)), + _ => unreachable!(), }, - } + } } -fn summary(handle: &Alpm) -> (String, (u64, u64)) { +fn summary(handle: &Alpm) -> (String, (u64, u64)) { let mut installed_size: i64 = 0; - let mut installed_size_old: i64 = 0; + let mut installed_size_old: i64 = 0; let mut download: i64 = 0; let mut files_to_download: u64 = 0; let mut current_line_len: usize = 0; @@ -175,44 +201,49 @@ fn summary(handle: &Alpm) -> (String, (u64, u64)) { let packages = if remove { handle.trans_remove() } else { handle.trans_add() }; let size = Term::size(&Term::stdout()); let preface = format!("Packages ({}) ", packages.len()); - let preface_newline = " ".repeat(preface.len()); + let preface_newline = " ".repeat(preface.len()); let line_delimiter = size.1 as usize - preface.len(); - let mut pkglist: String = String::new(); + let mut pkglist: String = String::new(); let mut summary = format!("\n{}{preface}{}", *BOLD, *RESET); - for pkg_sync in packages { + for pkg_sync in packages { let pkg = match handle.localdb().pkg(pkg_sync.name()) { - Ok(pkg) => pkg, Err(_) => pkg_sync, + Ok(pkg) => pkg, + Err(_) => pkg_sync, }; - let output = format!("{}-{}{}{} ", pkg.name(), *DIM, pkg_sync.version(), *RESET); + let output = format!("{}-{}{}{} ", pkg.name(), *DIM, pkg_sync.version(), *RESET); let download_size = pkg_sync.download_size(); let string_len = pkg.name().len() + pkg_sync.version().len() + 2; - if current_line_len+string_len >= line_delimiter { + if current_line_len + string_len >= line_delimiter { summary.push_str(&format!("{pkglist}\n")); pkglist = preface_newline.clone(); - current_line_len = pkglist.len(); + current_line_len = pkglist.len(); } current_line_len += string_len; - installed_size_old += pkg.isize(); + installed_size_old += pkg.isize(); installed_size += pkg_sync.isize(); - + if download_size > 0 { download += download_size; files_to_download += 1; } - pkglist.push_str(&output); + pkglist.push_str(&output); } - let total_str = if remove { "Total Removed Size" } else { "Total Installed Size" }; - let net = installed_size-installed_size_old; + let total_str = if remove { + "Total Removed Size" + } else { + "Total Installed Size" + }; + let net = installed_size - installed_size_old; + + summary.push_str(&format!("{pkglist}\n\n{}{total_str}{}: {}\n", *BOLD, *RESET, installed_size.to_byteunit(SI))); - summary.push_str(&format!("{pkglist}\n\n{}{total_str}{}: {}\n", *BOLD, *RESET, installed_size.to_byteunit(SI))); - if net != 0 { - summary.push_str(&format!("{}Net Upgrade Size{}: {}\n", *BOLD, *RESET, net.to_byteunit(SI))); + summary.push_str(&format!("{}Net Upgrade Size{}: {}\n", *BOLD, *RESET, net.to_byteunit(SI))); } if download > 0 { diff --git a/pacwrap-core/src/sync/transaction/prepare.rs b/pacwrap-core/src/sync/transaction/prepare.rs index 5098761..ff1d31f 100644 --- a/pacwrap-core/src/sync/transaction/prepare.rs +++ b/pacwrap-core/src/sync/transaction/prepare.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -16,35 +16,45 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -use crate::{err, - Error, - Result, - config::{InstanceHandle, InstanceType}, - sync::{self, - SyncError, - transaction::{Transaction, - TransactionState, - TransactionMode, - TransactionType, - TransactionHandle, +use crate::{ + config::{InstanceHandle, InstanceType}, + constants::UNIX_TIMESTAMP, + err, + sync::{ + self, + transaction::{ + SyncReqResult, + Transaction, TransactionAggregator, TransactionFlags, - SyncReqResult}}, constants::UNIX_TIMESTAMP}; + TransactionHandle, + TransactionMode, + TransactionState::{self, *}, + TransactionType::*, + }, + SyncError, + }, + Error, + Result, +}; pub struct Prepare { state: TransactionState, } -impl Transaction for Prepare { +impl Transaction for Prepare { fn new(new_state: TransactionState, _: &TransactionAggregator) -> Box { - Box::new(Self { - state: new_state, - }) + Box::new(Self { state: new_state }) } - fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result { + fn engage( + &self, + ag: &mut TransactionAggregator, + handle: &mut TransactionHandle, + inshandle: &InstanceHandle, + ) -> Result { match self.state { - TransactionState::Prepare => { + Prepare => { let deps: Vec<&str> = inshandle.metadata().dependencies(); let instype = inshandle.metadata().container_type(); let action = ag.action(); @@ -52,61 +62,64 @@ impl Transaction for Prepare { if deps.len() > 0 { for dep in deps.iter().rev() { match ag.cache().get_instance_option(dep) { - Some(dep_handle) => { + Some(dep_handle) => { let dep_handle = &sync::instantiate_alpm(dep_handle); let create = ag.flags().contains(TransactionFlags::CREATE); let timestamp = inshandle.metadata().timestamp(); let present = *UNIX_TIMESTAMP; handle.enumerate_package_lists(dep_handle, create && present == timestamp) - }, + } None => err!(SyncError::DependentContainerMissing(dep.to_string()))?, } - } + } } - if let TransactionType::Upgrade(upgrade, ..) = action { - if ! upgrade && handle.metadata().queue.len() == 0 { + if let Upgrade(upgrade, ..) = action { + if !upgrade && handle.metadata().queue.len() == 0 { err!(SyncError::NothingToDo(true))? } } else { if handle.metadata().queue.len() == 0 { err!(SyncError::NothingToDo(true))? - } + } } if handle.metadata().queue.len() == 0 { - if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Local) { - return Ok(TransactionState::UpToDate) + if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Local) { + return Ok(UpToDate); } } - if let TransactionType::Remove(..) = action { - Ok(TransactionState::Stage) - } else if let InstanceType::BASE = instype { - Ok(TransactionState::Stage) + if let Remove(..) = action { + Ok(Stage) + } else if let InstanceType::Base = instype { + Ok(Stage) } else { - Ok(TransactionState::PrepareForeign) + Ok(PrepareForeign(false)) } - }, - TransactionState::PrepareForeign => { - if let InstanceType::BASE = inshandle.metadata().container_type() { - return Ok(TransactionState::Complete(false)) + } + PrepareForeign(updated) => { + if let InstanceType::Base = inshandle.metadata().container_type() { + return Ok(Complete(false)); } - if ! ag.flags().contains(TransactionFlags::FORCE_DATABASE) { - if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Foreign) { + if !ag.flags().contains(TransactionFlags::FORCE_DATABASE) { + if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Foreign) { if ag.deps_updated(inshandle) { - return Ok(TransactionState::StageForeign) + return Ok(StageForeign); } - return Ok(TransactionState::Stage) + return match ag.action() { + Remove(..) => Ok(Complete(updated)), + Upgrade(..) => Ok(Stage), + }; } } - Ok(TransactionState::StageForeign) + Ok(StageForeign) } - _ => unreachable!() + _ => unreachable!(), } } } diff --git a/pacwrap-core/src/sync/transaction/stage.rs b/pacwrap-core/src/sync/transaction/stage.rs index 8d9176d..fdb0364 100644 --- a/pacwrap-core/src/sync/transaction/stage.rs +++ b/pacwrap-core/src/sync/transaction/stage.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -18,17 +18,24 @@ */ use alpm::TransFlag; -use crate::{err, - Error, Result, - config::{InstanceType, InstanceHandle}, - sync::{SyncError, - transaction::{Transaction, - TransactionState, - TransactionMode, - TransactionType, - TransactionHandle, +use crate::{ + config::{InstanceHandle, InstanceType}, + err, + sync::{ + transaction::{ + Transaction, TransactionAggregator, - TransactionFlags}}}; + TransactionFlags, + TransactionHandle, + TransactionMode::{self, *}, + TransactionState::{self, *}, + TransactionType::*, + }, + SyncError, + }, + Error, + Result, +}; pub struct Stage { state: TransactionState, @@ -36,31 +43,36 @@ pub struct Stage { flags: TransFlag, } -impl Transaction for Stage { - fn new(new: TransactionState, ag: &TransactionAggregator) -> Box { +impl Transaction for Stage { + fn new(new: TransactionState, ag: &TransactionAggregator) -> Box { let mut flag; let modeset; if let TransactionState::Stage = new { - modeset = TransactionMode::Local; + modeset = Local; flag = TransFlag::NO_DEP_VERSION; - + if ag.flags().contains(TransactionFlags::DATABASE_ONLY) { flag = flag | TransFlag::DB_ONLY; } } else { - modeset = TransactionMode::Foreign; + modeset = Foreign; flag = TransFlag::NO_DEP_VERSION | TransFlag::DB_ONLY; } - Box::new(Self { + Box::new(Self { state: new, flags: flag, - mode: modeset + mode: modeset, }) } - fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result { + fn engage( + &self, + ag: &mut TransactionAggregator, + handle: &mut TransactionHandle, + inshandle: &InstanceHandle, + ) -> Result { if let Err(error) = handle.alpm().trans_init(self.flags) { err!(SyncError::InitializationFailure(error.to_string().into()))? } @@ -71,43 +83,44 @@ impl Transaction for Stage { handle.set_flags(ag.flags(), self.flags); match ag.action() { - TransactionType::Upgrade(upgrade, downgrade, _) => { + Upgrade(upgrade, downgrade, _) => { if *upgrade { handle.alpm().sync_sysupgrade(*downgrade).unwrap(); } - handle.prepare(ag.action(), ag.flags())?; + handle.prepare(ag.action(), ag.flags())?; next_state(&self.state, check_keyring(ag, handle, inshandle)) - }, - TransactionType::Remove(_,_,_) => { + } + Remove(..) => { handle.prepare(ag.action(), ag.flags())?; next_state(&self.state, false) - }, + } } } } fn check_keyring(ag: &TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> bool { match inshandle.metadata().container_type() { - InstanceType::BASE => { + InstanceType::Base => { if ag.is_keyring_synced() { - return false + return false; } - - handle.alpm() + + handle + .alpm() .trans_add() .iter() .find_map(|a| Some(a.name() == "archlinux-keyring")) .unwrap_or(false) - }, - _ => false + } + _ => false, } } fn next_state(state: &TransactionState, option: bool) -> Result { Ok(match state { - TransactionState::Stage => TransactionState::Commit(option), - TransactionState::StageForeign => TransactionState::CommitForeign, - _ => unreachable!() + Stage => Commit(option), + StageForeign => CommitForeign, + _ => unreachable!(), }) } diff --git a/pacwrap-core/src/sync/transaction/uptodate.rs b/pacwrap-core/src/sync/transaction/uptodate.rs index cf2439b..5bc0637 100644 --- a/pacwrap-core/src/sync/transaction/uptodate.rs +++ b/pacwrap-core/src/sync/transaction/uptodate.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,23 +17,30 @@ * along with this program. If not, see . */ +use super::{ + Result, + Transaction, + TransactionAggregator, + TransactionHandle, + TransactionState::{self, Complete}, +}; use crate::{config::InstanceHandle, constants::ARROW_GREEN}; -use super::{Transaction, - TransactionState, - TransactionHandle, - TransactionAggregator, - Result}; pub struct UpToDate; impl Transaction for UpToDate { - fn new(_: TransactionState, _: &TransactionAggregator) -> Box { - Box::new(Self {}) + fn new(_: TransactionState, _: &TransactionAggregator) -> Box { + Box::new(Self {}) } - fn engage(&self, _: &mut TransactionAggregator, _: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result { + fn engage( + &self, + _: &mut TransactionAggregator, + _: &mut TransactionHandle, + inshandle: &InstanceHandle, + ) -> Result { let instance = inshandle.vars().instance(); - println!("{} {instance} is up-to-date!", *ARROW_GREEN); - Ok(TransactionState::Complete(false)) + println!("{} {instance} is up-to-date!", *ARROW_GREEN); + Ok(Complete(false)) } } diff --git a/pacwrap-core/src/sync/utils.rs b/pacwrap-core/src/sync/utils.rs index 151a851..1fcfd7a 100644 --- a/pacwrap-core/src/sync/utils.rs +++ b/pacwrap-core/src/sync/utils.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,18 +17,20 @@ * along with this program. If not, see . */ -use alpm::{CommitResult, FileConflictType, Package, Alpm, PrepareResult}; +use alpm::{Alpm, CommitResult, FileConflictType, Package, PrepareResult}; -use crate::{err, +use crate::{ + constants::{BOLD, BOLD_WHITE, RESET}, + err, + sync::SyncError, + utils::{print_error, print_warning}, Error, Result, - sync::SyncError, - constants::{BOLD, BOLD_WHITE, RESET}, - utils::{print_error, print_warning}}; +}; pub trait AlpmUtils { fn get_local_package(&self, pkg: &str) -> Option>; - fn get_package(&self, pkg: &str) -> Option>; + fn get_package(&self, pkg: &str) -> Option>; } impl AlpmUtils for Alpm { @@ -36,45 +38,34 @@ impl AlpmUtils for Alpm { if let Ok(pkg) = self.localdb().pkg(pkg) { return Some(pkg); } else { - self.localdb() - .pkgs() - .iter() - .find_map(|f| { - if f.provides() - .iter() - .filter(|d| pkg == d.name()) - .count() > 0 { + self.localdb().pkgs().iter().find_map(|f| { + if f.provides().iter().filter(|d| pkg == d.name()).count() > 0 { Some(f) } else { None - } + } }) } } fn get_package(&self, pkg: &str) -> Option> { - for sync in self.syncdbs() { + for sync in self.syncdbs() { if let Ok(pkg) = sync.pkg(pkg) { return Some(pkg); } else { - let package = sync.pkgs() - .iter() - .find_map(|f| { - if f.provides() - .iter() - .filter(|d| pkg == d.name()) - .count() > 0 { + let package = sync.pkgs().iter().find_map(|f| { + if f.provides().iter().filter(|d| pkg == d.name()).count() > 0 { Some(f) } else { None - } + } }); if let None = package { continue; } - return package + return package; } } @@ -91,53 +82,74 @@ pub fn erroneous_transaction<'a>(error: (CommitResult<'a>, alpm::Error)) -> Resu let file = conflict.file(); let target = conflict.target(); print_warning(format!("{}: '{}' already exists.", target, file)); - }, + } FileConflictType::Target => { let file = conflict.file(); - let target = format!("{}{}{}",*BOLD_WHITE, conflict.target(), *RESET); - if let Some(conflicting) = conflict.conflicting_target() { + let target = format!("{}{}{}", *BOLD_WHITE, conflict.target(), *RESET); + if let Some(conflicting) = conflict.conflicting_target() { let conflicting = format!("{}{conflicting}{}", *BOLD_WHITE, *RESET); - print_warning(format!("{conflicting}: '{target}' is owned by {file}")); + print_warning(format!("{conflicting}: '{target}' is owned by {file}")); } else { print_warning(format!("{target}: '{file}' is owned by foreign target")); } - }, + } } } err!(SyncError::TransactionFailure("Conflict within container filesystem".into()))? - }, - CommitResult::PkgInvalid(p) => { + } + CommitResult::PkgInvalid(p) => for pkg in p.iter() { let pkg = format!("{}{pkg}{}", *BOLD_WHITE, *RESET); - print_error(format!("Invalid package: {}", pkg)); - } - }, - _ => () + print_error(format!("Invalid package: {}", pkg)); + }, + _ => (), } err!(SyncError::TransactionFailure(error.1.to_string())) } -pub fn erroneous_preparation<'a>(error: (PrepareResult<'a>, alpm::Error)) -> Result<()> { +pub fn erroneous_preparation<'a>(error: (PrepareResult<'a>, alpm::Error)) -> Result<()> { match error.0 { - PrepareResult::PkgInvalidArch(list) => { - for package in list.iter() { - print_error(format!("Invalid architecture {}{}{} for {}{}{}", *BOLD, package.arch().unwrap(), *RESET, *BOLD, package.name(), *RESET)); - } - }, - PrepareResult::UnsatisfiedDeps(list) => { + PrepareResult::PkgInvalidArch(list) => + for package in list.iter() { + print_error(format!( + "Invalid architecture {}{}{} for {}{}{}", + *BOLD, + package.arch().unwrap(), + *RESET, + *BOLD, + package.name(), + *RESET + )); + }, + PrepareResult::UnsatisfiedDeps(list) => for missing in list.iter() { - print_error(format!("Unsatisifed dependency {}{}{} for target {}{}{}", *BOLD, missing.depend(), *RESET, *BOLD, missing.target(), *RESET)); - } - }, - PrepareResult::ConflictingDeps(list) => { + print_error(format!( + "Unsatisifed dependency {}{}{} for target {}{}{}", + *BOLD, + missing.depend(), + *RESET, + *BOLD, + missing.target(), + *RESET + )); + }, + PrepareResult::ConflictingDeps(list) => for conflict in list.iter() { - print_error(format!("Conflict between {}{}{} and {}{}{}: {}", *BOLD, conflict.package1(), *RESET, *BOLD, conflict.package2(), *RESET, conflict.reason())); - } - }, + print_error(format!( + "Conflict between {}{}{} and {}{}{}: {}", + *BOLD, + conflict.package1(), + *RESET, + *BOLD, + conflict.package2(), + *RESET, + conflict.reason() + )); + }, _ => (), } - + err!(SyncError::PreparationFailure(error.1.to_string())) } diff --git a/pacwrap-core/src/utils.rs b/pacwrap-core/src/utils.rs index 41bf863..87b8570 100644 --- a/pacwrap-core/src/utils.rs +++ b/pacwrap-core/src/utils.rs @@ -1,6 +1,6 @@ /* * pacwrap-core - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,41 +17,56 @@ * along with this program. If not, see . */ -use std::{path::Path, env::var, os::unix::net::UnixStream, fmt::Display, time::{SystemTime, UNIX_EPOCH}}; +use std::{ + env::var, + fmt::Display, + os::unix::net::UnixStream, + path::Path, + time::{SystemTime, UNIX_EPOCH}, +}; use nix::unistd::isatty; -use crate::{err, Error, ErrorKind, Result, constants::{BOLD_RED, BOLD_YELLOW, RESET, TERM, COLORTERM, UID, GID}}; +use crate::{ + constants::{BOLD_RED, BOLD_YELLOW, COLORTERM, GID, RESET, TERM, UID}, + err, + Error, + ErrorKind, + Result, +}; pub use arguments::Arguments; pub use termcontrol::TermControl; -pub mod termcontrol; pub mod arguments; pub mod prompt; +pub mod termcontrol; pub fn print_warning(message: impl Into + Display) { - eprintln!("{}warning:{} {}", *BOLD_YELLOW, *RESET, &message); -} + eprintln!("{}warning:{} {}", *BOLD_YELLOW, *RESET, &message); +} pub fn print_error(message: impl Into + Display) { eprintln!("{}error:{} {}", *BOLD_RED, *RESET, &message); -} +} pub fn env_var(env: &'static str) -> Result { match var(env) { Ok(var) => Ok(var), - Err(_) => err!(ErrorKind::EnvVarUnset(env)) + Err(_) => err!(ErrorKind::EnvVarUnset(env)), } } pub fn check_socket(socket: &String) -> bool { - match UnixStream::connect(&Path::new(socket)) { Ok(_) => true, Err(_) => false, } + match UnixStream::connect(&Path::new(socket)) { + Ok(_) => true, + Err(_) => false, + } } pub fn is_color_terminal() -> bool { let value = *TERM; - let is_dumb = ! value.is_empty() && value.to_lowercase() != "dumb"; + let is_dumb = !value.is_empty() && value.to_lowercase() != "dumb"; is_dumb && isatty(0).is_ok() && isatty(1).is_ok() } @@ -60,18 +75,14 @@ pub fn is_truecolor_terminal() -> bool { let value = COLORTERM.to_lowercase(); is_color_terminal() && value == "truecolor" || value == "24bit" - } pub fn unix_time_as_seconds() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() + SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() } pub fn read_le_32(vec: &Vec, pos: usize) -> u32 { - ((vec[pos+0] as u32) << 0) + ((vec[pos+1] as u32) << 8) + ((vec[pos+2] as u32) << 16) + ((vec[pos+3] as u32) << 24) + ((vec[pos + 0] as u32) << 0) + ((vec[pos + 1] as u32) << 8) + ((vec[pos + 2] as u32) << 16) + ((vec[pos + 3] as u32) << 24) } pub fn check_root() -> Result<()> { diff --git a/pacwrap-core/src/utils/arguments.rs b/pacwrap-core/src/utils/arguments.rs index 5914acc..7f764c4 100644 --- a/pacwrap-core/src/utils/arguments.rs +++ b/pacwrap-core/src/utils/arguments.rs @@ -1,7 +1,9 @@ -use std::fmt::{Display, Formatter}; -use std::env; +use std::{ + env, + fmt::{Display, Formatter}, +}; -use crate::{error::*, impl_error, err}; +use crate::{err, error::*, impl_error}; #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum Operand<'a> { @@ -10,7 +12,7 @@ pub enum Operand<'a> { Long(&'a str), LongPos(&'a str, &'a str), Value(&'a str), - None + Nothing, } #[derive(Debug)] @@ -33,9 +35,9 @@ impl_error!(InvalidArgument); impl Display for InvalidArgument { fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { + match self { Self::UnsuppliedOperand(params, message) => write!(fmter, "Option '{params}': {message}"), - Self::InvalidOperand(oper) => write!(fmter, "Invalid option '{oper}'"), + Self::InvalidOperand(oper) => write!(fmter, "Invalid option '{oper}'"), Self::OperationUnspecified => write!(fmter, "Operation not specified."), Self::TargetUnspecified => write!(fmter, "Target not specified."), }?; @@ -48,32 +50,35 @@ impl<'a> Arguments<'a> { pub fn new() -> Self { Self { values: env::args() - .skip(1) - .map(|a| { let a: &str = a.leak(); a }) - .collect::>(), + .skip(1) + .map(|a| { + let a: &str = a.leak(); + a + }) + .collect::>(), operands: Vec::new(), idx: 0, cur: 0, } } - pub fn populate(mut self) -> Arguments<'a> { - for string in &self.values { - match string { - string if string.starts_with("--") => { + pub fn populate(mut self) -> Arguments<'a> { + for string in &self.values { + match string { + string if string.starts_with("--") => if string.contains('=') { - let value: Vec<&'a str> = string[2..].splitn(2, '=').collect(); + let value: Vec<&'a str> = string[2 ..].splitn(2, '=').collect(); - self.operands.extend([Operand::Long(value[0]), Operand::LongPos(value[0], value[1])]); + self.operands.extend([Operand::Long(value[0]), Operand::LongPos(value[0], value[1])]); } else { - self.operands.push(Operand::Long(&string[2..])); - } - }, - string if string.starts_with("-") => if string.len() > 1 { - for operand in string[1..].chars() { - self.operands.push(Operand::Short(operand)); - } - }, + self.operands.push(Operand::Long(&string[2 ..])); + }, + string if string.starts_with("-") => + if string.len() > 1 { + for operand in string[1 ..].chars() { + self.operands.push(Operand::Short(operand)); + } + }, _ => self.operands.push(match self.operands.last() { Some(last) => match last { Operand::Short(c) => Operand::ShortPos(*c, string), @@ -90,15 +95,13 @@ impl<'a> Arguments<'a> { pub fn target(&mut self) -> Result<&'a str> { for op in self.into_iter() { - if let Operand::ShortPos(_, name) - | Operand::LongPos(_, name) - | Operand::Value(name) = op { + if let Operand::ShortPos(_, name) | Operand::LongPos(_, name) | Operand::Value(name) = op { return Ok(name); } } err!(InvalidArgument::TargetUnspecified) - } + } pub fn set_index(&mut self, index: usize) { self.idx = index; @@ -116,8 +119,8 @@ impl<'a> Arguments<'a> { &self.values } } - -impl <'a>Iterator for Arguments<'a> { + +impl<'a> Iterator for Arguments<'a> { type Item = Operand<'a>; fn next(&mut self) -> Option { @@ -126,35 +129,34 @@ impl <'a>Iterator for Arguments<'a> { if self.cur < self.operands.len() { self.idx += 1; Some(self.operands[self.cur]) - } else { + } else { self.set_index(0); None } } } -impl <'a>Default for &Operand<'a> { +impl<'a> Default for &Operand<'a> { fn default() -> Self { - &Operand::None + &Operand::Nothing } } -impl <'a>Default for Operand<'a> { +impl<'a> Default for Operand<'a> { fn default() -> Self { - Self::None + Self::Nothing } } -impl <'a>Display for Operand<'a> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl<'a> Display for Operand<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Operand::Long(str) => write!(fmt, "--{}", str), Operand::LongPos(str, eq) => write!(fmt, "--{}={}", str, eq), Operand::Short(char) => write!(fmt, "-{}", char), Operand::ShortPos(str, eq) => write!(fmt, "-{} {}", str, eq), Operand::Value(str) => write!(fmt, "{}", str), - Operand::None => write!(fmt, "None"), + Operand::Nothing => write!(fmt, "None"), } } } - diff --git a/pacwrap-core/src/utils/prompt.rs b/pacwrap-core/src/utils/prompt.rs index 28ecbf3..bfc674b 100644 --- a/pacwrap-core/src/utils/prompt.rs +++ b/pacwrap-core/src/utils/prompt.rs @@ -1,8 +1,11 @@ -use dialoguer::{theme::ColorfulTheme, Input, console::{style, Style}}; +use dialoguer::{ + console::{style, Style}, + theme::ColorfulTheme, + Input, +}; -pub fn prompt(prefix: &str, prompt: impl Into, yn_prompt: bool) -> Result<(),()> { - if let Ok(value) = create_prompt(prompt.into(), prefix, - if yn_prompt { "[Y/n]" } else { "[N/y]" }) { +pub fn prompt(prefix: &str, prompt: impl Into, yn_prompt: bool) -> Result<(), ()> { + if let Ok(value) = create_prompt(prompt.into(), prefix, if yn_prompt { "[Y/n]" } else { "[N/y]" }) { if value.to_lowercase() == "y" || (yn_prompt && value.is_empty()) { Ok(()) } else { @@ -19,14 +22,11 @@ fn create_prompt(message: String, prefix: &str, prompt: &str) -> Result * SPDX-License-Identifier: GPL-3.0-only * @@ -17,55 +17,51 @@ * along with this program. If not, see . */ -use nix::sys::termios::{Termios, - tcgetattr, - tcsetattr, - SetArg::TCSANOW}; +use nix::sys::termios::{tcgetattr, tcsetattr, SetArg::TCSANOW, Termios}; use crate::{err, Error, ErrorKind, Result}; /******* - * - * ermControl struct - * - * Impelments basic, portable functionalily for controlling terminal parameters. - * - ***/ + * + * ermControl struct + * + * Impelments basic, portable functionalily for controlling terminal parameters. + * + ***/ #[derive(Clone)] pub struct TermControl { tm: Option, - fd: i32 + fd: i32, } impl TermControl { /* - * A valid termios struct is presumed to be returned - * if there is a valid tty at specified fd. + * A valid termios struct is presumed to be returned + * if there is a valid tty at specified fd. * - * If the application is not being instantiated from a tty, + * If the application is not being instantiated from a tty, * then return TermControl with tm set with None. */ pub fn new(f: i32) -> Self { match tcgetattr(f) { - Ok(t) => Self { tm: Some(t), fd: f}, - Err(_) => Self { tm: None, fd: f} + Ok(t) => Self { tm: Some(t), fd: f }, + Err(_) => Self { tm: None, fd: f }, } } - /* - * Check if Termios initiated and then execute tcsetattr to reset terminal. - */ + /* + * Check if Termios initiated and then execute tcsetattr to reset terminal. + */ - pub fn reset_terminal(&self) -> Result<()> { + pub fn reset_terminal(&self) -> Result<()> { match self.tm.as_ref() { Some(tm) => match tcsetattr(self.fd, TCSANOW, tm) { Ok(_) => Ok(()), Err(errno) => err!(ErrorKind::Termios(errno)), }, - None => Ok(()) + None => Ok(()), } } } - diff --git a/pacwrap/build.rs b/pacwrap/build.rs index 5ac6d33..30447ba 100644 --- a/pacwrap/build.rs +++ b/pacwrap/build.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,32 +17,32 @@ * along with this program. If not, see . */ -use std::process::Command; -use std::env::var; +use std::{env::var, process::Command}; fn head() -> String { match Command::new("git").args(["rev-parse", "--short", "HEAD"]).output() { - Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()), + Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()), Err(_) => "N/A".into(), - } + } } fn time(debug: bool) -> String { match debug { false => match Command::new("git").args(["log", "-1", "--date=format:%d/%m/%Y", "--format=%ad"]).output() { - Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()), + Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()), Err(_) => "N/A".into(), }, true => match Command::new("date").args(["+%d/%m/%Y %T"]).output() { - Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()), + Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()), Err(_) => "N/A".into(), - } + }, } } fn release(debug: bool) -> &'static str { match debug { - true => "DEV", false => "RELEASE", + true => "DEV", + false => "RELEASE", } } @@ -51,7 +51,7 @@ fn is_debug() -> bool { } fn main() { - if ! cfg!(target_os="linux") || ! cfg!(target_family="unix") { + if !cfg!(target_os = "linux") || !cfg!(target_family = "unix") { panic!("Unsupported build target. Please refer to the documentation for further information.") } diff --git a/pacwrap/src/compat.rs b/pacwrap/src/compat.rs index 9191eb3..cad63d2 100644 --- a/pacwrap/src/compat.rs +++ b/pacwrap/src/compat.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,12 +19,14 @@ use std::process::Command; -use pacwrap_core::{config, +use pacwrap_core::{ + config, + err, + error::*, exec::utils::handle_process, - utils::arguments::{Arguments, Operand}, - ErrorKind, - error::*, - err}; + utils::arguments::{Arguments, Operand}, + ErrorKind, +}; fn save_configuration() -> Result<()> { err!(ErrorKind::Message("This function has been deprecated."))? @@ -35,10 +37,10 @@ fn print_configuration(instance: &str) -> Result<()> { let mut pkgs_string = String::new(); let mut depends_string = String::new(); - println!("INSTANCE_CONFIG[{},0]={}", instance, ins.metadata().container_type()); + println!("INSTANCE_CONFIG[{},0]={}", instance, ins.metadata().container_type()); for i in ins.metadata().dependencies() { - depends_string.push_str(&format!("{} ", i)); + depends_string.push_str(&format!("{} ", i)); } println!("INSTANCE_CONFIG[{},1]=\"{}\"", instance, depends_string); @@ -54,12 +56,10 @@ pub fn compat(args: &mut Arguments) -> Result<()> { match args.next().unwrap_or_default() { Operand::Short('s') | Operand::Long("save") => save_configuration(), Operand::Short('l') | Operand::Long("load") => print_configuration(args.target()?), - _ => args.invalid_operand() + _ => args.invalid_operand(), } } -pub fn execute_bash(executable: &'static str, args: &mut Arguments) -> Result<()> { - handle_process(&executable, Command::new(&executable) - .args(args.values()) - .spawn()) +pub fn execute_bash(executable: &'static str, args: &mut Arguments) -> Result<()> { + handle_process(&executable, Command::new(&executable).args(args.values()).spawn()) } diff --git a/pacwrap/src/exec.rs b/pacwrap/src/exec.rs index eca492a..8d45973 100644 --- a/pacwrap/src/exec.rs +++ b/pacwrap/src/exec.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,94 +17,100 @@ * along with this program. If not, see . */ -use std::{os::unix::{process::ExitStatusExt, io::AsRawFd}, - process::{Command, Child, ExitStatus, exit}, - fs::{File, remove_file}, +use std::{ + fs::{remove_file, File}, + os::unix::{io::AsRawFd, process::ExitStatusExt}, path::{Path, PathBuf}, + process::{exit, Child, Command, ExitStatus}, + thread, time::Duration, - vec::Vec, - thread}; + vec::Vec, +}; -use nix::{unistd::Pid, sys::signal::{Signal, kill}}; -use signal_hook::{consts::*, iterator::Signals}; use command_fds::{CommandFdExt, FdMapping}; +use nix::{ + sys::signal::{kill, Signal}, + unistd::Pid, +}; +use signal_hook::{consts::*, iterator::Signals}; -use pacwrap_core::{err, - ErrorKind, - error::*, - exec::{ExecutionError, - args::ExecutionArgs, - seccomp::{provide_bpf_program, configure_bpf_program}, - utils::{bwrap_json, execute_fakeroot_container}}, - constants::{self, - DEFAULT_PATH, - BWRAP_EXECUTABLE, - DBUS_PROXY_EXECUTABLE, - DBUS_SOCKET, - XDG_RUNTIME_DIR}, - config::{self, - Dbus, - InstanceHandle, +use pacwrap_core::{ + config::{ + self, + register::{register_dbus, register_filesystems, register_permissions}, + Dbus, + InstanceHandle, InstanceType, - register::{register_filesystems, - register_permissions, - register_dbus}}, - utils::{TermControl, - arguments::{Arguments, Operand, InvalidArgument}, - env_var, - print_warning, - check_root}}; + }, + constants::{self, BWRAP_EXECUTABLE, DBUS_PROXY_EXECUTABLE, DBUS_SOCKET, DEFAULT_PATH, XDG_RUNTIME_DIR}, + err, + error::*, + exec::{ + args::ExecutionArgs, + seccomp::{configure_bpf_program, provide_bpf_program}, + utils::{bwrap_json, execute_fakeroot_container}, + ExecutionError, + }, + utils::{ + arguments::{Arguments, InvalidArgument, Operand as Op}, + check_root, + env_var, + print_warning, + TermControl, + }, + ErrorKind, +}; const PROCESS_SLEEP_DURATION: Duration = Duration::from_millis(250); const SOCKET_SLEEP_DURATION: Duration = Duration::from_micros(500); enum ExecParams<'a> { - Root(i8, bool, Vec<&'a str>, InstanceHandle<'a>), + Root(i8, bool, Vec<&'a str>, InstanceHandle<'a>), Container(i8, bool, Vec<&'a str>, InstanceHandle<'a>), } /* - * Open an issue or possibly submit a PR if this module's argument parser + * Open an issue or possibly submit a PR if this module's argument parser * is conflicting with an application you use. */ -impl <'a>ExecParams<'a> { +impl<'a> ExecParams<'a> { fn parse(args: &'a mut Arguments) -> Result { - let runtime = args.values() - .iter() - .filter_map(|f| { - let str = *f; - - match str { - string if string.starts_with("-E") - | string.eq("--exec") - | string.eq("--target") - | string.eq("--verbose") - | string.eq("--shell") - | string.eq("--root") - | string.eq("--fake-chroot") => None, - _ => Some(str), - } - }) - .skip(1) - .collect(); let mut verbosity: i8 = 0; let mut shell = false; let mut root = false; let mut container = None; + let runtime = args + .values() + .iter() + .filter_map(|f| match *f { + string + if string.starts_with("-E") + | string.eq("--exec") + | string.eq("--target") + | string.eq("--verbose") + | string.eq("--shell") + | string.eq("--root") + | string.eq("--fake-chroot") => + None, + _ => Some(*f), + }) + .skip(1) + .collect(); while let Some(arg) = args.next() { match arg { - Operand::Short('r') | Operand::Long("root") => root = true, - Operand::Short('s') | Operand::Long("shell") => shell = true, - Operand::Short('v') | Operand::Long("verbose") => verbosity += 1, - Operand::ShortPos('E', str) - | Operand::ShortPos('s', str) - | Operand::ShortPos('r', str) - | Operand::ShortPos('v', str) - | Operand::LongPos("target", str) - | Operand::Value(str) => if let None = container { - container = Some(str); + Op::Short('r') | Op::Long("root") => root = true, + Op::Short('s') | Op::Long("shell") => shell = true, + Op::Short('v') | Op::Long("verbose") => verbosity += 1, + Op::ShortPos('E', str) + | Op::ShortPos('s', str) + | Op::ShortPos('r', str) + | Op::ShortPos('v', str) + | Op::LongPos("target", str) + | Op::Value(str) => + if let None = container { + container = Some(str); }, _ => continue, } @@ -115,8 +121,8 @@ impl <'a>ExecParams<'a> { None => err!(InvalidArgument::TargetUnspecified)?, }; - if let InstanceType::DEP = handle.metadata().container_type() { - err!(ErrorKind::Message("Execution upon sliced filesystems is not supported."))? + if let InstanceType::Slice = handle.metadata().container_type() { + err!(ErrorKind::Message("Execution in container filesystem segments is not supported."))? } Ok(match root { @@ -130,9 +136,9 @@ pub fn execute<'a>(args: &'a mut Arguments<'a>) -> Result<()> { check_root()?; match ExecParams::parse(args)? { - ExecParams::Root(verbosity, true, _, handle) => execute_fakeroot(&handle, vec!("bash"), verbosity), - ExecParams::Root(verbosity, false, args, handle) => execute_fakeroot(&handle, args, verbosity), - ExecParams::Container(verbosity, true, _, handle) => execute_container(&handle, vec!("bash"), true, verbosity), + ExecParams::Root(verbosity, true, _, handle) => execute_fakeroot(&handle, vec!["bash"], verbosity), + ExecParams::Root(verbosity, false, args, handle) => execute_fakeroot(&handle, args, verbosity), + ExecParams::Container(verbosity, true, _, handle) => execute_container(&handle, vec!["bash"], true, verbosity), ExecParams::Container(verbosity, false, args, handle) => execute_container(&handle, args, false, verbosity), } } @@ -142,85 +148,105 @@ fn execute_container<'a>(ins: &InstanceHandle, arguments: Vec<&str>, shell: bool let mut jobs: Vec = Vec::new(); let cfg = ins.config(); let vars = ins.vars(); - - if ! cfg.allow_forking() { - exec_args.push_env("--die-with-parent"); + + if !cfg.allow_forking() { + exec_args.push_env("--die-with-parent"); } - if ! cfg.enable_userns() { - exec_args.push_env("--unshare-user"); - exec_args.push_env("--disable-userns"); + if !cfg.enable_userns() { + exec_args.push_env("--unshare-user"); + exec_args.push_env("--disable-userns"); } else { print_warning("Namespace nesting has been known in the past to enable container escape vulnerabilities."); } - match ! cfg.retain_session() { + match !cfg.retain_session() { true => exec_args.push_env("--new-session"), - false => print_warning("Retaining a console session is known to allow for container escape. See CVE-2017-5226 for details."), + false => + print_warning("Retaining a console session is known to allow for container escape. See CVE-2017-5226 for details."), } - match shell && *constants::IS_COLOR_TERMINLAL { + match shell && *constants::IS_COLOR_TERMINLAL { true => exec_args.env("TERM", "xterm"), false => exec_args.env("TERM", "dumb"), - } + } - if cfg.dbus().len() > 0 { - jobs.push(instantiate_dbus_proxy(cfg.dbus(), &mut exec_args)?); + if cfg.dbus().len() > 0 { + jobs.push(instantiate_dbus_proxy(cfg.dbus(), &mut exec_args)?); } exec_args.env("XDG_RUNTIME_DIR", &*XDG_RUNTIME_DIR); register_filesystems(cfg.filesystem(), &vars, &mut exec_args)?; register_permissions(cfg.permissions(), &mut exec_args)?; - + let path = match exec_args.get_var("PATH") { - Some(var) => var.as_str(), None => { exec_args.env("PATH", DEFAULT_PATH); DEFAULT_PATH }, + Some(var) => var.as_str(), + None => { + exec_args.env("PATH", DEFAULT_PATH); + DEFAULT_PATH + } }; let path_vec: Vec<&str> = path.split(":").collect(); let (reader, writer) = os_pipe::pipe().unwrap(); let fd = writer.as_raw_fd(); let (sec_reader, sec_writer) = os_pipe::pipe().unwrap(); let sec_fd = match cfg.seccomp() { - true => provide_bpf_program(configure_bpf_program(cfg), &sec_reader, sec_writer).unwrap(), - false => { print_warning("Disabling seccomp filtering can allow for sandbox escape."); 0 }, + true => provide_bpf_program(configure_bpf_program(cfg), &sec_reader, sec_writer).unwrap(), + false => { + print_warning("Disabling seccomp filtering can allow for sandbox escape."); + 0 + } }; - let tc = TermControl::new(0); + let tc = TermControl::new(0); let mut proc = Command::new(BWRAP_EXECUTABLE); - proc.arg("--dir") + proc.env_clear() + .arg("--dir") .arg("/tmp") .args(exec_args.get_bind()) - .arg("--dev").arg("/dev") - .arg("--proc").arg("/proc") + .arg("--dev") + .arg("/dev") + .arg("--proc") + .arg("/proc") .args(exec_args.get_dev()) .arg("--unshare-all") - .arg("--clearenv") .args(exec_args.get_env()) .arg("--info-fd") .arg(fd.to_string()); let fd_mappings = match sec_fd { - 0 => vec![FdMapping { parent_fd: fd, child_fd: fd }], + 0 => vec![FdMapping { + parent_fd: fd, + child_fd: fd, + }], _ => { - proc.arg("--seccomp") - .arg(sec_fd.to_string()); - vec![FdMapping { parent_fd: fd, child_fd: fd }, - FdMapping { parent_fd: sec_fd, child_fd: sec_fd }] - }, + proc.arg("--seccomp").arg(sec_fd.to_string()); + vec![ + FdMapping { + parent_fd: fd, + child_fd: fd, + }, + FdMapping { + parent_fd: sec_fd, + child_fd: sec_fd, + }, + ] + } }; if verbosity == 1 { - println!("Arguments:\t {arguments:?}\n{ins:?}"); + eprintln!("Arguments:\t {arguments:?}\n{ins:?}"); } else if verbosity > 1 { - println!("Arguments:\t {arguments:?}\n{ins:?}\n{exec_args:?}"); + eprintln!("Arguments:\t {arguments:?}\n{ins:?}\n{exec_args:?}"); } check_path(ins, &arguments, path_vec)?; - proc.args(arguments).fd_mappings(fd_mappings).unwrap(); + proc.args(arguments).fd_mappings(fd_mappings).unwrap(); match proc.spawn() { Ok(c) => Ok(wait_on_process(c, bwrap_json(reader, writer)?, *cfg.allow_forking(), jobs, tc))?, - Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())), + Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())), } } @@ -228,10 +254,10 @@ fn signal_trap(bwrap_pid: i32) { let mut signals = Signals::new(&[SIGHUP, SIGINT, SIGQUIT, SIGTERM]).unwrap(); thread::spawn(move || { - let proc: &str = &format!("/proc/{}/", bwrap_pid); - let proc = Path::new(proc); + let proc: &str = &format!("/proc/{}/", bwrap_pid); + let proc = Path::new(proc); - for _ in signals.forever() { + for _ in signals.forever() { if proc.exists() { kill(Pid::from_raw(bwrap_pid), Signal::SIGKILL).unwrap(); } @@ -239,14 +265,14 @@ fn signal_trap(bwrap_pid: i32) { }); } -fn wait_on_process(mut process: Child, bwrap_pid: i32, block: bool, mut jobs: Vec, tc: TermControl) -> Result<()> { +fn wait_on_process(mut process: Child, bwrap_pid: i32, block: bool, mut jobs: Vec, tc: TermControl) -> Result<()> { signal_trap(bwrap_pid); - + match process.wait() { Ok(status) => { if block { - let proc: &str = &format!("/proc/{}/", bwrap_pid); - let proc = Path::new(proc); + let proc: &str = &format!("/proc/{}/", bwrap_pid); + let proc = Path::new(proc); while proc.exists() { thread::sleep(PROCESS_SLEEP_DURATION); @@ -256,14 +282,14 @@ fn wait_on_process(mut process: Child, bwrap_pid: i32, block: bool, mut jobs: Ve for job in jobs.iter_mut() { job.kill().unwrap(); } - + if let Err(err) = cleanup(&tc) { err.warn(); } process_exit(status) - }, - Err(error) => err!(ErrorKind::ProcessWaitFailure(BWRAP_EXECUTABLE, error.kind())) + } + Err(error) => err!(ErrorKind::ProcessWaitFailure(BWRAP_EXECUTABLE, error.kind())), } } @@ -276,10 +302,10 @@ fn cleanup(tc: &TermControl) -> Result<()> { fn process_exit(status: ExitStatus) -> Result<()> { exit(match status.code() { Some(status) => status, - None => { - eprint!("\nbwrap process {status}"); - println!(); - 100+status.signal().unwrap_or(0) + None => { + eprint!("\nbwrap process {status}"); + println!(); + 100 + status.signal().unwrap_or(0) } }); } @@ -292,18 +318,21 @@ fn instantiate_dbus_proxy(per: &Vec>, args: &mut ExecutionArgs) -> let dbus_session = env_var("DBUS_SESSION_BUS_ADDRESS")?; match Command::new(DBUS_PROXY_EXECUTABLE) - .arg(dbus_session).arg(&*DBUS_SOCKET) - .args(args.get_dbus()).spawn() { + .arg(dbus_session) + .arg(&*DBUS_SOCKET) + .args(args.get_dbus()) + .spawn() + { Ok(mut child) => { let mut increment: u8 = 0; - + args.robind(&*DBUS_SOCKET, &dbus_socket_path); args.symlink(&dbus_socket_path, "/run/dbus/system_bus_socket"); args.env("DBUS_SESSION_BUS_ADDRESS", format!("unix:path={dbus_socket_path}")); - - /* - * This blocking code is required to prevent a downstream race condition with - * bubblewrap. Unless xdg-dbus-proxy is passed improper parameters, this while loop + + /* + * This blocking code is required to prevent a downstream race condition with + * bubblewrap. Unless xdg-dbus-proxy is passed improper parameters, this while loop * shouldn't almost ever increment more than once or twice. * * With a sleep duration of 500 microseconds, we check the socket 200 times before failure. @@ -312,18 +341,18 @@ fn instantiate_dbus_proxy(per: &Vec>, args: &mut ExecutionArgs) -> * to wait on a FD prior to instantiating the filesystem bindings. */ - while ! check_socket(&*DBUS_SOCKET, &increment, &mut child)? { + while !check_socket(&*DBUS_SOCKET, &increment, &mut child)? { increment += 1; } Ok(child) - }, - Err(error) => err!(ErrorKind::ProcessInitFailure(DBUS_PROXY_EXECUTABLE, error.kind()))? + } + Err(error) => err!(ErrorKind::ProcessInitFailure(DBUS_PROXY_EXECUTABLE, error.kind()))?, } } fn check_socket(socket: &String, increment: &u8, process_child: &mut Child) -> Result { - if increment == &200 { + if increment == &200 { process_child.kill().ok(); clean_up_socket(&*DBUS_SOCKET)?; err!(ExecutionError::SocketTimeout(socket.into()))? @@ -336,11 +365,11 @@ fn check_socket(socket: &String, increment: &u8, process_child: &mut Child) -> R fn create_placeholder(path: &str) -> Result<()> { match File::create(path) { Ok(file) => Ok(drop(file)), - Err(error) => err!(ErrorKind::IOError(path.into(), error.kind())) + Err(error) => err!(ErrorKind::IOError(path.into(), error.kind())), } } -fn clean_up_socket(path: &str) -> Result<()> { +fn clean_up_socket(path: &str) -> Result<()> { if let Err(error) = remove_file(path) { match error.kind() { std::io::ErrorKind::NotFound => return Ok(()), @@ -351,12 +380,12 @@ fn clean_up_socket(path: &str) -> Result<()> { Ok(()) } -fn execute_fakeroot(ins: &InstanceHandle, arguments: Vec<&str>, verbosity: i8) -> Result<()> { +fn execute_fakeroot(ins: &InstanceHandle, arguments: Vec<&str>, verbosity: i8) -> Result<()> { if verbosity > 0 { - println!("Arguments:\t {arguments:?}\n{ins:?}\n"); + eprintln!("Arguments:\t {arguments:?}\n{ins:?}\n"); } - check_path(ins, &arguments, vec!("/usr/bin", "/bin"))?; + check_path(ins, &arguments, vec!["/usr/bin", "/bin"])?; execute_fakeroot_container(ins, arguments) } @@ -369,9 +398,12 @@ fn check_path(ins: &InstanceHandle, args: &Vec<&str>, path: Vec<&str>) -> Result let root = ins.vars().root().as_ref(); for dir in path { - match Path::new(&format!("{}/{}",root,dir)).try_exists() { - Ok(_) => if dest_exists(root, dir, exec)? { return Ok(()) }, - Err(error) => err!(ExecutionError::InvalidPathVar(dir.into(), error.kind()))? + match Path::new(&format!("{}/{}", root, dir)).try_exists() { + Ok(_) => + if dest_exists(root, dir, exec)? { + return Ok(()); + }, + Err(error) => err!(ExecutionError::InvalidPathVar(dir.into(), error.kind()))?, } } @@ -399,18 +431,18 @@ fn dest_exists(root: &str, dir: &str, exec: &str) -> Result { } else if let Ok(path) = path_direct.read_link() { if let Some(path) = path.as_os_str().to_str() { return dest_exists(root, dir, path); - } - } + } + } Ok(path.exists() | path_direct.exists()) } fn obtain_path(path: &Path, exec: &str) -> Result { match Path::canonicalize(&path) { - Ok(path) => Ok(path), + Ok(path) => Ok(path), Err(err) => match err.kind() { std::io::ErrorKind::NotFound => Ok(path.to_path_buf()), _ => err!(ErrorKind::IOError(exec.into(), err.kind()))?, - } + }, } } diff --git a/pacwrap/src/main.rs b/pacwrap/src/main.rs index cf7167c..195fb78 100644 --- a/pacwrap/src/main.rs +++ b/pacwrap/src/main.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,22 +19,22 @@ use pacwrap_core::utils::arguments::{Arguments, Operand}; -mod sync; -mod remove; -mod query; -mod exec; mod compat; +mod exec; mod manual; +mod query; +mod remove; +mod sync; fn main() { let arguments = &mut Arguments::new().populate(); let result = match arguments.next().unwrap_or_default() { Operand::Short('E') | Operand::Long("exec") => exec::execute(arguments), - Operand::Short('S') | Operand::Long("sync") => sync::synchronize(arguments), + Operand::Short('S') | Operand::Long("sync") => sync::synchronize(arguments), Operand::Short('R') | Operand::Long("remove") => remove::remove(arguments), Operand::Short('Q') | Operand::Long("query") => query::query(arguments), Operand::Short('U') | Operand::Long("utils") => compat::execute_bash("pacwrap-utils", arguments), - Operand::Short('P') | Operand::Long("proc") => compat::execute_bash("pacwrap-ps", arguments), + Operand::Short('P') | Operand::Long("proc") => compat::execute_bash("pacwrap-ps", arguments), Operand::Short('h') | Operand::Long("help") => manual::help(arguments), Operand::Short('V') | Operand::Long("version") => manual::print_version(arguments), Operand::Long("compat") => compat::compat(arguments), diff --git a/pacwrap/src/manual.rs b/pacwrap/src/manual.rs index d2652a9..dd684eb 100644 --- a/pacwrap/src/manual.rs +++ b/pacwrap/src/manual.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,26 +19,27 @@ use indexmap::IndexSet; use lazy_static::lazy_static; -use std::fmt::{Write, Formatter, Display}; +use std::fmt::{Display, Formatter, Write}; -use pacwrap_core::{err, +use pacwrap_core::{ + err, impl_error, + utils::{arguments::Operand, is_color_terminal, is_truecolor_terminal, Arguments}, Error, ErrorTrait, - utils::{Arguments, - arguments::Operand, - is_color_terminal, - is_truecolor_terminal}}; +}; lazy_static! { - static ref HELP_ALL: Vec = - [HelpTopic::Execute, + static ref HELP_ALL: Vec = [ + HelpTopic::Execute, HelpTopic::Sync, HelpTopic::Process, HelpTopic::Utils, HelpTopic::Help, - HelpTopic::Version, - HelpTopic::Copyright].into(); + HelpTopic::Version, + HelpTopic::Copyright + ] + .into(); } #[derive(Debug)] @@ -63,11 +64,12 @@ pub fn help(mut args: &mut Arguments) -> Result<(), Error> { let mut buffer = String::new(); for topic in help.0 { - topic.write(&mut buffer, help.1).unwrap(); + topic.write(&mut buffer, help.1).unwrap(); } match help.1 { - HelpLayout::Console => print!("\x1b[?7l{buffer}\x1b[?7h"), _ => print!("{buffer}"), + HelpLayout::Console => print!("\x1b[?7l{buffer}\x1b[?7h"), + _ => print!("{buffer}"), } Ok(()) @@ -75,91 +77,64 @@ pub fn help(mut args: &mut Arguments) -> Result<(), Error> { fn ascertain_help<'a>(args: &mut Arguments) -> Result<(IndexSet<&'a HelpTopic>, &'a HelpLayout), Error> { let mut layout = match is_color_terminal() { - true => &HelpLayout::Console, false => &HelpLayout::Dumb, + true => &HelpLayout::Console, + false => &HelpLayout::Dumb, }; - let mut topic: Vec<&HelpTopic> = vec!(&HelpTopic::Default); + let mut topic: Vec<&HelpTopic> = vec![&HelpTopic::Default]; let mut more = false; - while let Some(arg) = args.next() { + while let Some(arg) = args.next() { match arg { - Operand::Long("format") - | Operand::Long("help") - | Operand::Short('f') - | Operand::Short('h') - => continue, - Operand::Short('m') - | Operand::Long("more") - => more = true, - Operand::LongPos("format", "dumb") - | Operand::ShortPos('f', "dumb") - => layout = &HelpLayout::Dumb, - Operand::LongPos("format", "markdown") - | Operand::ShortPos('f', "markdown") - => layout = &HelpLayout::Markdown, - Operand::LongPos("format", "man") - | Operand::ShortPos('f', "man") - => layout = &HelpLayout::Man, - Operand::LongPos("format", "ansi") - | Operand::ShortPos('f', "ansi") - => layout = &HelpLayout::Console, + Operand::Long("format") | Operand::Long("help") | Operand::Short('f') | Operand::Short('h') => continue, + Operand::Short('m') | Operand::Long("more") => more = true, + Operand::ShortPos('f', "man") | Operand::LongPos("format", "man") => layout = &HelpLayout::Man, + Operand::ShortPos('f', "ansi") | Operand::LongPos("format", "ansi") => layout = &HelpLayout::Console, + Operand::ShortPos('f', "dumb") | Operand::LongPos("format", "dumb") => layout = &HelpLayout::Dumb, + Operand::ShortPos('f', "markdown") | Operand::LongPos("format", "markdown") => layout = &HelpLayout::Markdown, Operand::ShortPos('h', "sync") - | Operand::ShortPos('h', "S") - | Operand::LongPos("help", "sync") - | Operand::LongPos("help", "S") - => topic.push(&HelpTopic::Sync), + | Operand::ShortPos('h', "S") + | Operand::LongPos("help", "sync") + | Operand::LongPos("help", "S") => topic.push(&HelpTopic::Sync), Operand::ShortPos('h', "E") - | Operand::ShortPos('h', "exec") - | Operand::LongPos("help", "E") - | Operand::LongPos("help", "exec") - => topic.push(&HelpTopic::Execute), + | Operand::ShortPos('h', "exec") + | Operand::LongPos("help", "E") + | Operand::LongPos("help", "exec") => topic.push(&HelpTopic::Execute), Operand::ShortPos('h', "process") - | Operand::ShortPos('h', "P") - | Operand::LongPos("help", "process") - | Operand::LongPos("help", "P") - => topic.push(&HelpTopic::Process), + | Operand::ShortPos('h', "P") + | Operand::LongPos("help", "process") + | Operand::LongPos("help", "P") => topic.push(&HelpTopic::Process), Operand::ShortPos('h', "utils") - | Operand::ShortPos('h', "U") - | Operand::LongPos("help", "utils") - | Operand::LongPos("help", "U") - => topic.push(&HelpTopic::Utils), + | Operand::ShortPos('h', "U") + | Operand::LongPos("help", "utils") + | Operand::LongPos("help", "U") => topic.push(&HelpTopic::Utils), Operand::ShortPos('h', "help") - | Operand::ShortPos('h', "h") - | Operand::LongPos("help", "help") - | Operand::LongPos("help", "h") - => topic.push(&HelpTopic::Help), - Operand::ShortPos('h', "synopsis") - | Operand::LongPos("help", "synopsis") - => topic.push(&HelpTopic::Default), + | Operand::ShortPos('h', "h") + | Operand::LongPos("help", "help") + | Operand::LongPos("help", "h") => topic.push(&HelpTopic::Help), Operand::ShortPos('h', "V") - | Operand::ShortPos('h', "version") - | Operand::LongPos("help", "V") - | Operand::LongPos("help", "version") - => topic.push(&HelpTopic::Version), - Operand::ShortPos('h', "copyright") - | Operand::LongPos("help", "copyright") - => topic.push(&HelpTopic::Copyright), - Operand::ShortPos('h', "all") - | Operand::LongPos("help", "all") - | Operand::Short('a') - | Operand::Long("all") - => topic.extend(HELP_ALL.iter()), - Operand::ShortPos('h', topic) - | Operand::LongPos("help", topic) - => err!(ErrorKind::InvalidTopic(topic.into()))?, - _ => args.invalid_operand()?, + | Operand::ShortPos('h', "version") + | Operand::LongPos("help", "V") + | Operand::LongPos("help", "version") => topic.push(&HelpTopic::Version), + Operand::ShortPos('h', "copyright") | Operand::LongPos("help", "copyright") => topic.push(&HelpTopic::Copyright), + Operand::ShortPos('h', "synopsis") | Operand::LongPos("help", "synopsis") => topic.push(&HelpTopic::Default), + Operand::ShortPos('h', "all") | Operand::LongPos("help", "all") | Operand::Short('a') | Operand::Long("all") => + topic.extend(HELP_ALL.iter()), + Operand::ShortPos('h', topic) | Operand::LongPos("help", topic) => err!(ErrorKind::InvalidTopic(topic.into()))?, + _ => args.invalid_operand()?, } } let len = topic.len(); - let start = if more || len == 1 || len > 7 { 0 } else { 1 }; + let start = if more || len == 1 || len > 7 { 0 } else { 1 }; args.set_index(1); - Ok((topic.drain(start..).collect(), layout)) + Ok((topic.drain(start ..).collect(), layout)) } fn minimal(args: &mut Arguments) -> bool { - match args.next().unwrap_or_default() { - Operand::LongPos("version", "min") | Operand::ShortPos('V', "min") => true, _ => false + match args.next().unwrap_or_default() { + Operand::LongPos("version", "min") | Operand::ShortPos('V', "min") => true, + _ => false, } } @@ -172,14 +147,14 @@ enum HelpTopic { Process, Help, Copyright, - Version + Version, } -enum HelpLayout { +enum HelpLayout { Man, Dumb, Markdown, - Console + Console, } impl HelpLayout { @@ -196,7 +171,7 @@ impl HelpLayout { match self { Self::Console => " ", Self::Markdown => "* **", - Self::Man => ".TP\n\\fB", + Self::Man => ".TP\n\\fB", Self::Dumb => " ", } } @@ -204,17 +179,15 @@ impl HelpLayout { fn sub_text(&self) -> &str { match self { Self::Console | Self::Dumb => " ", - Self::Man => ".PP\n", - Self::Markdown => "", + Self::Man => ".PP\n", + Self::Markdown => "", } } fn reset(&self) -> &str { match self { Self::Console => "", - Self::Markdown - | Self::Man - | Self::Dumb => "" + Self::Markdown | Self::Man | Self::Dumb => "", } } @@ -223,7 +196,7 @@ impl HelpLayout { Self::Console => "", Self::Man => "\\fP", Self::Markdown => "**", - Self::Dumb => "" + Self::Dumb => "", } } @@ -247,21 +220,21 @@ impl HelpLayout { impl HelpTopic { fn write(&self, buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { match self { - Self::Default => default(buf,layout), - Self::Sync => sync(buf,layout), - Self::Execute => execute(buf,layout), - Self::Process => process(buf,layout), - Self::Utils => utils(buf,layout), - Self::Help => meta(buf,layout), - Self::Copyright => copyright(buf,layout), - Self::Version => version(buf,layout), + Self::Default => default(buf, layout), + Self::Sync => sync(buf, layout), + Self::Execute => execute(buf, layout), + Self::Process => process(buf, layout), + Self::Utils => utils(buf, layout), + Self::Help => meta(buf, layout), + Self::Copyright => copyright(buf, layout), + Self::Version => version(buf, layout), } } } fn default(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { let head = layout.head(); - let tab = layout.tab(); + let tab = layout.tab(); let sub = layout.sub(); let bold = layout.bold(); let reset = layout.reset(); @@ -269,20 +242,28 @@ fn default(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> let name = env!("CARGO_PKG_NAME"); match layout { - HelpLayout::Man => writeln!(buf, ".nh\n.TH {name} 1 \"{}-{} ({})\" {name} \"User Manual\"\n", - env!("CARGO_PKG_VERSION"), - env!("PACWRAP_BUILDSTAMP"), - env!("PACWRAP_BUILDTIME"))?, - HelpLayout::Markdown => writeln!(buf, "# Pacwrap User Manual + HelpLayout::Man => writeln!( + buf, + ".nh\n.TH {name} 1 \"{}-{} ({})\" {name} \"User Manual\"\n", + env!("CARGO_PKG_VERSION"), + env!("PACWRAP_BUILDSTAMP"), + env!("PACWRAP_BUILDTIME") + )?, + HelpLayout::Markdown => writeln!( + buf, + "# Pacwrap User Manual -This document was generated by the {name} binary on {} with version {}-{} of the program.\n", - env!("PACWRAP_BUILDTIME"), - env!("CARGO_PKG_VERSION"), - env!("PACWRAP_BUILDSTAMP"))?, - _ => () +This document was generated by the {name} binary on {} with version {}-{} of the program.\n", + env!("PACWRAP_BUILDTIME"), + env!("CARGO_PKG_VERSION"), + env!("PACWRAP_BUILDSTAMP") + )?, + _ => (), } - writeln!(buf, "{head}NAME{reset} + writeln!( + buf, + "{head}NAME{reset} {tab}pacwrap - Command-line application which facilitates the creation, management, and execution of unprivileged, {tab}sandboxed containers with bubblewrap and libalpm. @@ -307,7 +288,8 @@ This document was generated by the {name} binary on {} with version {}-{} of the {tab}{tab}Invoke a printout of this manual to {bold}STDOUT{reset_bold}. {sub}-V, --version{reset_bold} -{tab}{tab}Display version and copyright information in {bold}STDOUT{reset_bold}.\n") +{tab}{tab}Display version and copyright information in {bold}STDOUT{reset_bold}.\n" + ) } fn execute(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { @@ -317,13 +299,16 @@ fn execute(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> let reset = layout.reset(); let reset_bold = layout.reset_bold(); - writeln!(buf, "{head}EXECUTE{reset} + writeln!( + buf, + "{head}EXECUTE{reset} {sub}-r, --root{reset_bold} {tab}{tab}Execute operation with fakeroot and fakechroot. Facilitates a command with faked privileges. {sub}-s, --shell{reset_bold} -{tab}{tab}Invoke a bash shell\n") +{tab}{tab}Invoke a bash shell\n" + ) } fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { @@ -334,7 +319,9 @@ fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { let reset_bold = layout.reset_bold(); let tab = layout.tab(); - writeln!(buf, "{head}HELP{reset} + writeln!( + buf, + "{head}HELP{reset} {sub}-m, --more{reset_bold} {tab}{tab}When specifying a topic to display, show the default topic in addition to specified options. @@ -345,7 +332,8 @@ fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { {tab}{tab}outside the context of package maintenance. 'man' option produces troff-formatted documents for man pages. {sub}-a, --all, --help=all{reset_bold} -{tab}{tab}Display all help topics.\n") +{tab}{tab}Display all help topics.\n" + ) } fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { @@ -353,10 +341,12 @@ fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { let bold = layout.bold(); let tab = layout.tab(); let sub = layout.sub(); - let reset = layout.reset(); + let reset = layout.reset(); let reset_bold = layout.reset_bold(); - writeln!(buf, "{head}SYNCHRONIZATION{reset} + writeln!( + buf, + "{head}SYNCHRONIZATION{reset} {sub}-y, --refresh{reset_bold} {tab}{tab}Synchronize remote package databases. Specify up to 2 times to force a refresh. @@ -377,29 +367,36 @@ fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { {sub}-b, --base{reset_bold} {tab}{tab}Base container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation. +{tab}{tab} {tab}{tab}This container type is used as the base layer for all downstream containers. Only one base container -{tab}{tab}dependency per slice or per root is supported. Filesystem and package deduplication via slices -{tab}{tab}and root containers is recommended, but optional. +{tab}{tab}dependency per slice or aggregate is supported. Filesystem and package deduplication via slices and +{tab}{tab}aggregate containers are recommended, but optional. {sub}-s, --slice{reset_bold} {tab}{tab}Slice container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation. -{tab}{tab}Requires a base dependency be specified, and one or more sliced dependencies, in order to ascertain -{tab}{tab}foreign packages and influence ordering of downstream synchronization target(s). Container slicing -{tab}{tab}provides the ability to install packages in a lightweight, sliced filesytem, which aid in the -{tab}{tab}deduplication of common downstream package and filesystem dependencies e.g. graphics drivers, -{tab}{tab}graphical toolkits, fonts, etc.. +{tab}{tab} +{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, to ascertain foreign +{tab}{tab}packages and influence ordering of downstream synchronization target(s). Container slicing provies +{tab}{tab}the ability to install packages in a lightweight, sliced filesytem, which aid in the deduplication +{tab}{tab}of common downstream package and filesystem dependencies. +{tab}{tab} +{tab}{tab}Useful for graphics drivers, graphical toolkits, fonts, etc.; these are not meant for applications. -{sub}-r, --root{reset_bold} -{tab}{tab}Root container type. Specify alongside {bold}-c, --create{reset_bold} to this assign container type during creation. -{tab}{tab}Requires a base dependency be specified, and optionally one or more sliced dependencies, in order -{tab}{tab}to ascertain foreign packages and influence ordering of this target. These containers are ideal -{tab}{tab}for installing software in with the least amount of filesystem and package synchronization overhead. +{sub}-a, --aggegrate{reset_bold} +{tab}{tab}Aggregate container type. Specify alongside {bold}-c, --create{reset_bold} to this assign container type during creation. +{tab}{tab} +{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, in order to acertain foreign +{tab}{tab}packages and amalgamate the target. These containers are ideal for installing software with the aid of +{tab}{tab}filesystem and package deduplication. +{tab}{tab} +{tab}{tab}Useful for all general purpose applications, browsers, e-mail clients, and even terminal user interface +{tab}{tab}applications such as IRC clients. It is recommended to base your containers on aggregate type containers. {sub}-t, --target=TARGET{reset_bold} {tab}{tab}Specify a target container for the specified operation. {sub}-d, --dep=DEPEND{reset_bold} -{tab}{tab}Specify a dependency container for the specified operation. +{tab}{tab}Specify a dependent container for the specified operation. {sub}-o, --target-only{reset_bold} {tab}{tab}Apply specified operation on the specified target only. @@ -413,17 +410,20 @@ fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { {tab}{tab}Transact on resident containers with a database-only transaction. {sub}--noconfirm{reset_bold} -{tab}{tab}Override confirmation prompts and confirm all operations.\n") - +{tab}{tab}Override confirmation prompts and confirm all operations.\n" + ) } fn process(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { let head = layout.head(); let tab = layout.tab(); let reset = layout.reset(); - - writeln!(buf, "{head}PROCESS{reset} -{tab}{tab}-TODO-\n") + + writeln!( + buf, + "{head}PROCESS{reset} +{tab}{tab}-TODO-\n" + ) } fn utils(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { @@ -431,8 +431,11 @@ fn utils(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { let tab = layout.tab(); let reset = layout.reset(); - writeln!(buf, "{head}UTILITIES{reset} -{tab}{tab}-TODO-\n") + writeln!( + buf, + "{head}UTILITIES{reset} +{tab}{tab}-TODO-\n" + ) } fn version(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { @@ -449,14 +452,17 @@ fn version(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> let release = env!("PACWRAP_BUILD"); let version_num = env!("CARGO_PKG_VERSION"); - writeln!(buf, "{head}VERSION{reset} + writeln!( + buf, + "{head}VERSION{reset} {sub}-V, --version, --version=min{reset_bold} {tab}{tab}Sends version information to {bold}STDOUT{reset_bold} with colourful ASCII art. {tab}{tab}The 'min' option provides a minimalistic output as is provided to non-colour terms. {sub_text}This documentation was generated by {name} v{version_num}-{suffix}-{release} ({timestamp}). -{tab}Please seek relevant documentation if '{name} -V' mismatches with the aforementioned.\n") +{tab}Please seek relevant documentation if '{name} -V' mismatches with the aforementioned.\n" + ) } fn copyright(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> { @@ -464,22 +470,25 @@ fn copyright(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Erro let tab = layout.tab(); let reset = layout.reset(); - writeln!(buf, "{head}COPYRIGHT{reset} + writeln!( + buf, + "{head}COPYRIGHT{reset} {tab}{tab}Copyright (C) 2023-2024 Xavier R.M. {tab}{tab}This program may be freely redistributed under the -{tab}{tab}terms of the GNU General Public License v3 only.\n") +{tab}{tab}terms of the GNU General Public License v3 only.\n" + ) } pub fn print_version(mut args: &mut Arguments) -> Result<(), Error> { - let name = env!("CARGO_PKG_NAME"); - let version = env!("CARGO_PKG_VERSION"); + let name = env!("CARGO_PKG_NAME"); + let version = env!("CARGO_PKG_VERSION"); let suffix = env!("PACWRAP_BUILDSTAMP"); let timestamp = env!("PACWRAP_BUILDTIME"); let release = env!("PACWRAP_BUILD"); - if ! minimal(&mut args) && is_truecolor_terminal() { + if !minimal(&mut args) && is_truecolor_terminal() { println!("\x1b[?7l\n  RPAACWWRRAAP   RAPPPACCCCCCCCCCCCCCCAPAR   RAPPPAC#####CCCCCCCCCCCCCCCCCCCCCCAPAR  @@ -502,14 +511,16 @@ pub fn print_version(mut args: &mut Arguments) -> Result<(), Error> { RPCCCCCCCPPR  RPCAPA \n\x1b[?7h"); } else { - println!("{name} v{version}-{suffix}-{release} ({timestamp}) + println!( + "{name} v{version}-{suffix}-{release} ({timestamp}) Copyright (C) 2023-2024 Xavier R.M. Website: https://pacwrap.sapphirus.org/ Github: https://github.com/sapphirusberyl/pacwrap This program may be freely redistributed under the -terms of the GNU General Public License v3 only.\n"); +terms of the GNU General Public License v3 only.\n" + ); } Ok(()) } diff --git a/pacwrap/src/query.rs b/pacwrap/src/query.rs index 8240c4e..360ec55 100644 --- a/pacwrap/src/query.rs +++ b/pacwrap/src/query.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,14 +17,19 @@ * along with this program. If not, see . */ -use alpm::{Alpm, PackageReason}; +use alpm::PackageReason; -use pacwrap_core::{config, - constants::{RESET, BOLD_GREEN}, - utils::arguments::{Operand, InvalidArgument}, - utils::{arguments::Arguments, check_root}, - error::*, - err}; +use pacwrap_core::{ + config, + constants::{BOLD_GREEN, RESET}, + err, + error::*, + sync::instantiate_alpm, + utils::{ + arguments::{Arguments, InvalidArgument, Operand}, + check_root, + }, +}; pub fn query(arguments: &mut Arguments) -> Result<()> { let mut target = ""; @@ -47,19 +52,18 @@ pub fn query(arguments: &mut Arguments) -> Result<()> { } let handle = config::provide_handle(target)?; - let root = handle.vars().root().as_ref(); - let handle = Alpm::new2(root, &format!("{}/var/lib/pacman/", root)).unwrap(); + let handle = instantiate_alpm(&handle); for pkg in handle.localdb().pkgs() { if explicit && pkg.reason() != PackageReason::Explicit { continue; } - + match quiet { true => println!("{} ", pkg.name()), - false => println!("{} {}{}{} ", pkg.name(), *BOLD_GREEN, pkg.version(), *RESET), - } + false => println!("{} {}{}{} ", pkg.name(), *BOLD_GREEN, pkg.version(), *RESET), + } } - + Ok(()) } diff --git a/pacwrap/src/remove.rs b/pacwrap/src/remove.rs index 530b8d8..d66a7c9 100644 --- a/pacwrap/src/remove.rs +++ b/pacwrap/src/remove.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -19,16 +19,22 @@ use std::collections::HashMap; -use pacwrap_core::{err, +use pacwrap_core::{ + config::{cache, init::init}, + err, error::*, log::Logger, - sync::transaction::TransactionType, - utils::{arguments::Operand, check_root}, - utils::arguments::{Arguments, InvalidArgument}, - sync::transaction::{TransactionFlags, TransactionAggregator}, - config::{cache, init::init}}; + sync::transaction::{TransactionAggregator, TransactionFlags, TransactionType}, + utils::{ + arguments::{Arguments, InvalidArgument, Operand as Op}, + check_root, + }, +}; pub fn remove(mut args: &mut Arguments) -> Result<()> { + check_root()?; + init()?; + let mut logger = Logger::new("pacwrap-sync").init().unwrap(); let action = { let mut recursive = 0; @@ -36,82 +42,64 @@ pub fn remove(mut args: &mut Arguments) -> Result<()> { while let Some(arg) = args.next() { match arg { - Operand::Short('s') | Operand::Long("recursive") => recursive += 1, - Operand::Short('c') | Operand::Long("cascade") => cascade = true, + Op::Short('s') | Op::Long("recursive") => recursive += 1, + Op::Short('c') | Op::Long("cascade") => cascade = true, _ => continue, } } - TransactionType::Remove(recursive > 0 , cascade, recursive > 1) + TransactionType::Remove(recursive > 0, cascade, recursive > 1) }; - check_root()?; - init()?; engage_aggregator(action, &mut args, &mut logger) } -fn engage_aggregator<'a>( - action_type: TransactionType, - args: &'a mut Arguments, - log: &'a mut Logger) -> Result<()> { +fn engage_aggregator<'a>(action_type: TransactionType, args: &'a mut Arguments, log: &'a mut Logger) -> Result<()> { let cache = cache::populate()?; let mut action_flags = TransactionFlags::NONE; let mut targets = Vec::new(); - let mut queue: HashMap<&'a str,Vec<&'a str>> = HashMap::new(); + let mut queue: HashMap<&'a str, Vec<&'a str>> = HashMap::new(); let mut current_target = None; - if let Operand::None = args.next().unwrap_or_default() { + if let Op::Nothing = args.next().unwrap_or_default() { err!(InvalidArgument::OperationUnspecified)? } while let Some(arg) = args.next() { match arg { - Operand::Long("remove") - | Operand::Long("cascade") - | Operand::Long("recursive") - | Operand::Short('R') - | Operand::Short('c') - | Operand::Short('s') - | Operand::Short('t') => continue, - Operand::Long("noconfirm") - => action_flags = action_flags | TransactionFlags::NO_CONFIRM, - Operand::Short('p') - | Operand::Long("preview") - => action_flags = action_flags | TransactionFlags::PREVIEW, - Operand::Long("dbonly") - => action_flags = action_flags | TransactionFlags::DATABASE_ONLY, - Operand::Long("force-foreign") - => action_flags = action_flags | TransactionFlags::FORCE_DATABASE, - Operand::Short('f') - | Operand::Long("filesystem") - => action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC, - Operand::ShortPos('t', target) - | Operand::LongPos("target", target) - | Operand::ShortPos(_, target) => { + Op::Long("remove") + | Op::Long("cascade") + | Op::Long("recursive") + | Op::Short('R') + | Op::Short('c') + | Op::Short('s') + | Op::Short('t') => continue, + Op::Long("dbonly") => action_flags = action_flags | TransactionFlags::DATABASE_ONLY, + Op::Long("noconfirm") => action_flags = action_flags | TransactionFlags::NO_CONFIRM, + Op::Long("force-foreign") => action_flags = action_flags | TransactionFlags::FORCE_DATABASE, + Op::Short('p') | Op::Long("preview") => action_flags = action_flags | TransactionFlags::PREVIEW, + Op::Short('f') | Op::Long("filesystem") => action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC, + Op::ShortPos('t', target) | Op::LongPos("target", target) | Op::ShortPos(_, target) => { cache.get_instance(target)?; current_target = Some(target); targets.push(target); - }, - Operand::LongPos(_, package) - | Operand::Value(package) => if let Some(target) = current_target { - match queue.get_mut(target) { - Some(vec) => vec.push(package), - None => { queue.insert(target, vec!(package)); }, - } - }, + } + Op::LongPos(_, package) | Op::Value(package) => + if let Some(target) = current_target { + match queue.get_mut(target) { + Some(vec) => vec.push(package), + None => { + queue.insert(target, vec![package]); + } + } + }, _ => args.invalid_operand()?, } } - + if let None = current_target { err!(InvalidArgument::TargetUnspecified)? } - Ok(TransactionAggregator::new(&cache, - queue, - log, - action_flags, - action_type, - current_target) - .aggregate()?) + Ok(TransactionAggregator::new(&cache, queue, log, action_flags, action_type, current_target).aggregate()?) } diff --git a/pacwrap/src/sync.rs b/pacwrap/src/sync.rs index ebe6875..3a5173d 100644 --- a/pacwrap/src/sync.rs +++ b/pacwrap/src/sync.rs @@ -1,6 +1,6 @@ /* * pacwrap - * + * * Copyright (C) 2023-2024 Xavier R.M. * SPDX-License-Identifier: GPL-3.0-only * @@ -17,30 +17,31 @@ * along with this program. If not, see . */ -use std::{collections::HashMap, - fs::File, - io::Write}; +use std::{collections::HashMap, fs::File, io::Write}; use indexmap::IndexMap; -use pacwrap_core::{err, - ErrorKind, +use pacwrap_core::{ + config::{cache, init::init, InstanceCache, InstanceHandle, InstanceType}, + constants::{ARROW_GREEN, BAR_GREEN, BOLD, RESET}, + err, error::*, log::Logger, - sync::transaction::TransactionType, - utils::check_root, - utils::arguments::{Arguments, - InvalidArgument, - Operand}, - config::{InstanceType, - InstanceHandle, - init::init, - cache}, - config::InstanceCache, - sync::{instantiate_trust, transaction::{TransactionFlags, TransactionAggregator}}, - constants::{BAR_GREEN, BOLD, RESET, ARROW_GREEN}}; + sync::{ + instantiate_trust, + transaction::{TransactionAggregator, TransactionFlags, TransactionType}, + }, + utils::{ + arguments::{Arguments, InvalidArgument::*, Operand as Op}, + check_root, + }, + ErrorKind, +}; pub fn synchronize(args: &mut Arguments) -> Result<()> { - let mut logger = Logger::new("pacwrap-sync").init().unwrap(); + check_root()?; + init()?; + + let mut logger = Logger::new("pacwrap-sync").init().unwrap(); let mut cache = cache::populate()?; let action = { let mut u = 0; @@ -48,8 +49,8 @@ pub fn synchronize(args: &mut Arguments) -> Result<()> { while let Some(arg) = args.next() { match arg { - Operand::Short('y') | Operand::Long("refresh") => y += 1, - Operand::Short('u') | Operand::Long("upgrade") => u += 1, + Op::Short('y') | Op::Long("refresh") => y += 1, + Op::Short('u') | Op::Long("upgrade") => u += 1, _ => continue, } } @@ -57,21 +58,18 @@ pub fn synchronize(args: &mut Arguments) -> Result<()> { TransactionType::Upgrade(u > 0, y > 0, y > 1) }; - check_root()?; - init()?; - - if create(args) { - if let TransactionType::Upgrade(upgrade, refresh, _) = action { - if ! refresh { - err!(InvalidArgument::UnsuppliedOperand("--refresh", "Required for container creation."))? - } else if ! upgrade { - err!(InvalidArgument::UnsuppliedOperand("--upgrade", "Required for container creation."))? + if create(args) { + if let TransactionType::Upgrade(upgrade, refresh, _) = action { + if !refresh { + err!(UnsuppliedOperand("--refresh", "Required for container creation."))? + } else if !upgrade { + err!(UnsuppliedOperand("--upgrade", "Required for container creation."))? } } - instantiate_trust()?; + instantiate_trust()?; instantiate(&mut logger, &mut cache, acquire_depends(args)?)?; - } + } engage_aggregator(&cache, action, args, &mut logger) } @@ -81,72 +79,80 @@ fn acquire_depends<'a>(args: &mut Arguments<'a>) -> Result match deps.get_mut(current_target) { - Some(d) => { - if let Some(instype) = instype { - if let InstanceType::BASE = instype { - err!(ErrorKind::Message("Dependencies cannot be assigned to base containers."))? - } + Op::Short('b') | Op::Long("base") => instype = Some(InstanceType::Base), + Op::Short('s') | Op::Long("slice") => instype = Some(InstanceType::Slice), + Op::Short('a') | Op::Long("agg") => instype = Some(InstanceType::Aggregate), + Op::ShortPos('d', dep) | Op::LongPos("dep", dep) => match instype { + Some(instance) => { + if let InstanceType::Base = instance { + err!(ErrorKind::Message("Dependencies cannot be assigned to base containers."))? } - - d.1.push(dep); - }, - None => err!(InvalidArgument::TargetUnspecified)? + + match dep.contains(",") { + true => + for dep in dep.split(",") { + match deps.get_mut(current_target) { + Some(d) => d.1.push(dep), + None => err!(TargetUnspecified)?, + } + }, + false => match deps.get_mut(current_target) { + Some(d) => d.1.push(dep), + None => err!(TargetUnspecified)?, + }, + } + } + None => err!(TargetUnspecified)?, + }, + Op::ShortPos('t', target) | Op::LongPos("target", target) => match instype { + Some(instype) => { + current_target = target; + deps.insert(current_target, (instype, vec![])); + } + None => err!(ErrorKind::Message("Container type not specified."))?, }, - Operand::Short('b') - | Operand::Long("base") => instype = Some(InstanceType::BASE), - Operand::Short('s') - | Operand::Long("slice") => instype = Some(InstanceType::DEP), - Operand::Short('r') - | Operand::Long("root") => instype = Some(InstanceType::ROOT), - Operand::ShortPos('t', target) - | Operand::LongPos("target", target) => match instype { - Some(instype) => { - current_target = target; - deps.insert(current_target, (instype, vec!())); - }, - None => err!(ErrorKind::Message("Container type not specified."))?, - }, _ => continue, } } for dep in deps.iter() { - if let InstanceType::BASE = dep.1.0 { + if let InstanceType::Base = dep.1 .0 { continue; - } - - if dep.1.1.is_empty() { - err!(ErrorKind::Message("Dependencies not specified."))? + } + + if dep.1 .1.is_empty() { + err!(ErrorKind::Message("Dependencies not specified."))? } } if current_target.len() == 0 { - err!(InvalidArgument::TargetUnspecified)? + err!(TargetUnspecified)? } Ok(deps) } - fn create(args: &mut Arguments) -> bool { - for arg in args { - if let Operand::Short('c') | Operand::Long("create") = arg { + for arg in args { + if let Op::Short('c') | Op::Long("create") = arg { return true; - } + } } return false; } -fn instantiate<'a>(logger: &mut Logger, cache: &mut InstanceCache<'a>, targets: IndexMap<&'a str, (InstanceType, Vec<&'a str>)>) -> Result<()> { - println!("{} {}Instantiating container...{}{}", *BAR_GREEN, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET); +fn instantiate<'a>( + logger: &mut Logger, + cache: &mut InstanceCache<'a>, + targets: IndexMap<&'a str, (InstanceType, Vec<&'a str>)>, +) -> Result<()> { + println!("{} {}Instantiating container{}...{}", *BAR_GREEN, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET); for target in targets { - cache.add(target.0, target.1.0, target.1.1)?; + cache.add(target.0, target.1 .0, target.1 .1)?; instantiate_container(logger, cache.get_instance(target.0)?)?; } @@ -158,10 +164,10 @@ fn instantiate_container<'a>(logger: &mut Logger, handle: &'a InstanceHandle<'a> let instype = handle.metadata().container_type(); if let Err(err) = std::fs::create_dir(handle.vars().root()) { - err!(ErrorKind::IOError(handle.vars().root().into(), err.kind()))? + err!(ErrorKind::IOError(handle.vars().root().into(), err.kind()))? } - if let InstanceType::ROOT | InstanceType::BASE = instype { + if let InstanceType::Aggregate | InstanceType::Base = instype { if let Err(err) = std::fs::create_dir(handle.vars().home()) { if err.kind() != std::io::ErrorKind::AlreadyExists { err!(ErrorKind::IOError(handle.vars().root().into(), err.kind()))? @@ -169,13 +175,14 @@ fn instantiate_container<'a>(logger: &mut Logger, handle: &'a InstanceHandle<'a> } let bashrc = format!("{}/.bashrc", handle.vars().home()); - + match File::create(&bashrc) { - Ok(mut f) => if let Err(error) = write!(f, "PS1=\"$USER> \"") { - err!(ErrorKind::IOError(bashrc, error.kind()))? - }, - Err(error) => err!(ErrorKind::IOError(bashrc.clone(), error.kind()))? - }; + Ok(mut f) => + if let Err(error) = write!(f, "PS1=\"$USER> \"") { + err!(ErrorKind::IOError(bashrc, error.kind()))? + }, + Err(error) => err!(ErrorKind::IOError(bashrc.clone(), error.kind()))?, + }; } handle.save()?; @@ -186,94 +193,85 @@ fn instantiate_container<'a>(logger: &mut Logger, handle: &'a InstanceHandle<'a> fn engage_aggregator<'a>( cache: &InstanceCache<'a>, - action_type: TransactionType, - args: &'a mut Arguments, - log: &'a mut Logger) -> Result<()> { + action_type: TransactionType, + args: &'a mut Arguments, + log: &'a mut Logger, +) -> Result<()> { let mut action_flags = TransactionFlags::NONE; let mut targets = Vec::new(); - let mut queue: HashMap<&'a str ,Vec<&'a str>> = HashMap::new(); + let mut queue: HashMap<&'a str, Vec<&'a str>> = HashMap::new(); let mut current_target = ""; let mut base = false; - if let Operand::None = args.next().unwrap_or_default() { - err!(InvalidArgument::OperationUnspecified)? + if let Op::Nothing = args.next().unwrap_or_default() { + err!(OperationUnspecified)? } while let Some(arg) = args.next() { match arg { - Operand::Short('d') - | Operand::Long("dep") | Operand::LongPos("dep", _) - | Operand::Short('s') | Operand::Long("slice") - | Operand::Short('r') | Operand::Long("root") - | Operand::Short('t') | Operand::Long("target") - | Operand::Short('y') | Operand::Long("refresh") - | Operand::Short('u') | Operand::Long("upgrade") => continue, - Operand::Short('o') - | Operand::Long("target-only") - => action_flags = action_flags | TransactionFlags::TARGET_ONLY, - Operand::Short('f') - | Operand::Long("filesystem") - => action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC, - Operand::Short('p') - | Operand::Long("preview") - => action_flags = action_flags | TransactionFlags::PREVIEW, - Operand::Short('c') - | Operand::Long("create") - => action_flags = action_flags | TransactionFlags::CREATE - | TransactionFlags::FORCE_DATABASE, - Operand::Short('b') | - Operand::Long("base") => base = true, - Operand::Long("dbonly") - => action_flags = action_flags | TransactionFlags::DATABASE_ONLY, - Operand::Long("force-foreign") - => action_flags = action_flags | TransactionFlags::FORCE_DATABASE, - Operand::Long("noconfirm") - => action_flags = action_flags | TransactionFlags::NO_CONFIRM, - Operand::ShortPos('t', target) - | Operand::LongPos("target", target) => { + Op::Short('a') + | Op::Short('s') + | Op::Short('d') + | Op::Short('t') + | Op::Short('y') + | Op::Short('u') + | Op::Long("aggregate") + | Op::Long("slice") + | Op::Long("dep") + | Op::Long("target") + | Op::Long("refresh") + | Op::Long("upgrade") + | Op::LongPos("dep", _) => continue, + Op::Short('b') | Op::Long("base") => base = true, + Op::Short('o') | Op::Long("target-only") => action_flags = action_flags | TransactionFlags::TARGET_ONLY, + Op::Short('f') | Op::Long("filesystem") => action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC, + Op::Short('p') | Op::Long("preview") => action_flags = action_flags | TransactionFlags::PREVIEW, + Op::Long("dbonly") => action_flags = action_flags | TransactionFlags::DATABASE_ONLY, + Op::Long("force-foreign") => action_flags = action_flags | TransactionFlags::FORCE_DATABASE, + Op::Long("noconfirm") => action_flags = action_flags | TransactionFlags::NO_CONFIRM, + Op::Short('c') | Op::Long("create") => + action_flags = action_flags | TransactionFlags::CREATE | TransactionFlags::FORCE_DATABASE, + Op::ShortPos('t', target) | Op::LongPos("target", target) => { cache.get_instance(target)?; current_target = target; targets.push(target); - if base { - queue.insert(current_target.into(), vec!("base", "pacwrap-base-dist")); - base = false; + if base { + queue.insert(current_target.into(), vec!["base", "pacwrap-base-dist"]); + base = false; } - }, - Operand::LongPos(_, package) - | Operand::Value(package) => if current_target != "" { - match queue.get_mut(current_target.into()) { - Some(vec) => vec.push(package.into()), - None => { queue.insert(current_target.into(), vec!(package)); }, - } - }, - _ => args.invalid_operand()? + } + Op::LongPos(_, package) | Op::Value(package) => + if current_target != "" { + match queue.get_mut(current_target.into()) { + Some(vec) => vec.push(package.into()), + None => { + queue.insert(current_target.into(), vec![package]); + } + } + }, + _ => args.invalid_operand()?, } } let current_target = match action_flags.intersects(TransactionFlags::TARGET_ONLY) { true => { - if current_target == "" && ! action_flags.intersects(TransactionFlags::FILESYSTEM_SYNC) { - err!(InvalidArgument::TargetUnspecified)? + if current_target == "" && !action_flags.intersects(TransactionFlags::FILESYSTEM_SYNC) { + err!(TargetUnspecified)? } Some(current_target) - }, + } false => { if let TransactionType::Upgrade(upgrade, refresh, _) = action_type { - if ! upgrade && ! refresh { - err!(InvalidArgument::OperationUnspecified)? + if !upgrade && !refresh { + err!(OperationUnspecified)? } } - + None } }; - Ok(TransactionAggregator::new(cache, - queue, - log, - action_flags, - action_type, - current_target).aggregate()?) + Ok(TransactionAggregator::new(cache, queue, log, action_flags, action_type, current_target).aggregate()?) }