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
This commit is contained in:
parent
3b5951cea6
commit
9b495390d2
71 changed files with 2781 additions and 2398 deletions
16
.rustfmt.toml
Normal file
16
.rustfmt.toml
Normal file
|
@ -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
|
|
@ -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"
|
||||
|
|
11
dist/tools/clean.sh
vendored
11
dist/tools/clean.sh
vendored
|
@ -18,10 +18,13 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
|
|
11
dist/tools/runtime.sh
vendored
11
dist/tools/runtime.sh
vendored
|
@ -21,10 +21,13 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-agent
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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.")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-agent
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<u8>) -> 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<u8>) -> Result<()> {
|
|||
fn deserialize<T: for<'de> Deserialize<'de>>(stdin: &mut File) -> Result<T> {
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-agent
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-agent
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,20 +17,25 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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.")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,35 +17,42 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ConfigError> for String {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn provide_handle(instance: &str) -> Result<InstanceHandle> {
|
||||
pub fn provide_handle(instance: &str) -> Result<InstanceHandle> {
|
||||
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<InstanceHandle> {
|
|||
handle(instance, InsVars::new(instance))
|
||||
}
|
||||
|
||||
fn save<T: Serialize>(obj: &T, path: &str) -> Result<()> {
|
||||
fn save<T: Serialize>(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<InstanceHandle<'a>> {
|
|||
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<Global> {
|
|||
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()))?,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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::<ConfigError>() {
|
||||
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<InstanceType>) -> 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<InstanceCache<'a>> {
|
|||
populate_from(&roots()?)
|
||||
}
|
||||
|
||||
fn roots<'a>() -> Result<Vec<&'a str>> {
|
||||
fn roots<'a>() -> Result<Vec<&'a str>> {
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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<String>
|
||||
address: Vec<String>,
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
},
|
||||
},
|
||||
&_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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/*");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<String>
|
||||
path: Vec<String>,
|
||||
}
|
||||
|
||||
#[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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String>
|
||||
struct System {
|
||||
#[serde(skip_serializing_if = "is_default_path", default = "default_path")]
|
||||
path: Vec<String>,
|
||||
}
|
||||
|
||||
#[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<String>) -> bool {
|
|||
}
|
||||
|
||||
fn default_path() -> Vec<String> {
|
||||
vec!("block".into(),
|
||||
"bus".into(),
|
||||
"class".into(),
|
||||
"dev".into(),
|
||||
"devices".into())
|
||||
vec!["block".into(), "bus".into(), "class".into(), "dev".into(), "devices".into()]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#![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<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
filesystem: Vec<Mount>
|
||||
pub struct ToHome {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default, rename = "volumes")]
|
||||
mounts: Vec<Mount>,
|
||||
}
|
||||
|
||||
#[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<String>
|
||||
#[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<String>) {
|
||||
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)))?
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#![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<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
filesystem: Vec<Mount>
|
||||
pub struct ToRoot {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default, rename = "volumes")]
|
||||
mounts: Vec<Mount>,
|
||||
}
|
||||
|
||||
#[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<String>
|
||||
#[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<String>) {
|
||||
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<String
|
|||
}
|
||||
|
||||
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)))?
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<String>,
|
||||
#[serde(default = "hold_pkg")]
|
||||
hold_pkg: Vec<String>,
|
||||
#[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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,14 +17,16 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,33 +17,39 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Box<dyn Filesystem>>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
permissions: Vec<Box<dyn Permission>>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
dbus: Vec<Box<dyn Dbus>>,
|
||||
dbus: Vec<Box<dyn Dbus>>,
|
||||
}
|
||||
|
||||
impl InstanceRuntime {
|
||||
pub fn new() -> Self {
|
||||
let default_fs: [Box<dyn Filesystem>; 2] = [Box::new(ROOT {}), Box::new(HOME {})];
|
||||
let default_per: [Box<dyn Permission>; 1] = [Box::new(NONE {})];
|
||||
let default_fs: [Box<dyn Filesystem>; 2] = [Box::new(Root {}), Box::new(Home {})];
|
||||
let default_per: [Box<dyn Permission>; 1] = [Box::new(None {})];
|
||||
let fs: Vec<Box<dyn Filesystem>> = Vec::from(default_fs);
|
||||
let per: Vec<Box<dyn Permission>> = Vec::from(default_per);
|
||||
|
||||
let per: Vec<Box<dyn Permission>> = 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<Box<dyn Permission>> {
|
||||
&self.permissions
|
||||
pub fn permissions(&self) -> &Vec<Box<dyn Permission>> {
|
||||
&self.permissions
|
||||
}
|
||||
|
||||
pub fn filesystem(&self) -> &Vec<Box<dyn Filesystem>> {
|
||||
&self.filesystems
|
||||
|
||||
pub fn filesystem(&self) -> &Vec<Box<dyn Filesystem>> {
|
||||
&self.filesystems
|
||||
}
|
||||
|
||||
pub fn dbus(&self) -> &Vec<Box<dyn Dbus>> {
|
||||
&self.dbus
|
||||
}
|
||||
|
||||
pub fn allow_forking(&self) -> &bool {
|
||||
&self.allow_forking
|
||||
|
||||
pub fn dbus(&self) -> &Vec<Box<dyn Dbus>> {
|
||||
&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<Cow<'a, str>>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
#[serde(default)]
|
||||
container_type: InstanceType,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
dependencies: Vec<Cow<'a, str>>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
explicit_packages: Vec<Cow<'a, str>>,
|
||||
#[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()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Option<Condition>, 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),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<String>
|
||||
struct Dev {
|
||||
devices: Vec<String>,
|
||||
}
|
||||
|
||||
#[typetag::serde]
|
||||
impl Permission for DEV {
|
||||
fn check(&self) -> Result<Option<Condition>, PermError> {
|
||||
#[typetag::serde(name = "dev")]
|
||||
impl Permission for Dev {
|
||||
fn check(&self) -> Result<Option<Condition>, 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));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Option<Condition>, 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<Option<Condition>, 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<Option<Condition>, PermError> {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn validate_xorg_socket() -> Result<Option<Condition>, PermError> {
|
||||
if ! X11_DISPLAY.is_empty() {
|
||||
fn validate_xorg_socket() -> Result<Option<Condition>, 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<Option<Condition>, 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Var>
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
variables: Vec<Var>,
|
||||
}
|
||||
|
||||
#[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<Option<Condition>, 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,45 +17,48 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String> = populate_dev();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct GPU;
|
||||
struct Graphics;
|
||||
|
||||
#[typetag::serde]
|
||||
impl Permission for GPU {
|
||||
fn check(&self) -> Result<Option<Condition>, PermError> {
|
||||
if ! Path::new("/dev").exists() {
|
||||
#[typetag::serde(name = "gpu")]
|
||||
impl Permission for Graphics {
|
||||
fn check(&self) -> Result<Option<Condition>, 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<String> {
|
|||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Option<Condition>, PermError> {
|
||||
Ok(Some(Success))
|
||||
}
|
||||
|
@ -38,6 +42,6 @@ impl Permission for NET {
|
|||
}
|
||||
|
||||
fn module(&self) -> &'static str {
|
||||
"NET"
|
||||
"net"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Option<Condition>, PermError> {
|
||||
Ok(Some(Success))
|
||||
#[typetag::serde(name = "none")]
|
||||
impl Permission for None {
|
||||
fn check(&self) -> Result<Option<Condition>, PermError> {
|
||||
Ok(Some(Success))
|
||||
}
|
||||
|
||||
fn register(&self, _: &mut ExecutionArgs) {}
|
||||
|
||||
fn module(&self) -> &'static str {
|
||||
"NONE"
|
||||
fn module(&self) -> &'static str {
|
||||
"none"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Option<Condition>, PermError> {
|
||||
if ! Path::new(&self.socket).exists() {
|
||||
#[typetag::serde(name = "pipewire")]
|
||||
impl Permission for Pipewire {
|
||||
fn check(&self) -> Result<Option<Condition>, 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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Option<Condition>, PermError> {
|
||||
if ! Path::new(&self.socket).exists() {
|
||||
#[typetag::serde(name = "pulseaudio")]
|
||||
impl Permission for Pulseaudio {
|
||||
fn check(&self) -> Result<Option<Condition>, 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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<Box<dyn Filesystem>>, 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<Box<dyn Permission>>, 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(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,9 +17,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 { "[1;31m->[0m" } else { "->" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;31m->[0m"
|
||||
} else {
|
||||
"->"
|
||||
}
|
||||
}
|
||||
|
||||
fn arrow_cyan() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;36m->[0m" } else { "->" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;36m->[0m"
|
||||
} else {
|
||||
"->"
|
||||
}
|
||||
}
|
||||
|
||||
fn arrow_green() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;32m->[0m" } else { "->" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;32m->[0m"
|
||||
} else {
|
||||
"->"
|
||||
}
|
||||
}
|
||||
|
||||
fn bar_green() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;32m::[0m" } else { "::" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;32m::[0m"
|
||||
} else {
|
||||
"::"
|
||||
}
|
||||
}
|
||||
|
||||
fn bar_cyan() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;36m::[0m" } else { "::" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;36m::[0m"
|
||||
} else {
|
||||
"::"
|
||||
}
|
||||
}
|
||||
|
||||
fn dim() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[2m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[2m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn bold() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn bold_white() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;37m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;37m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn bold_red() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;31m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;31m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn bold_yellow() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;33m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;33m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn bold_green() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[1;32m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[1;32m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn reset() -> &'static str {
|
||||
if *IS_COLOR_TERMINLAL { "[0m" } else { "" }
|
||||
if *IS_COLOR_TERMINLAL {
|
||||
"[0m"
|
||||
} 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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,9 +17,16 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<T> = std::result::Result<T, Error>;
|
||||
|
||||
|
@ -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<dyn ErrorTrait>) -> Self {
|
||||
Self {
|
||||
kind: err,
|
||||
}
|
||||
Self { kind: err }
|
||||
}
|
||||
|
||||
pub fn handle(&self) {
|
||||
|
@ -73,8 +78,8 @@ impl Error {
|
|||
match self.downcast::<SyncError>() {
|
||||
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<T: 'static>(&self) -> std::result::Result<&T, &Self> {
|
||||
match self.kind.as_any().downcast_ref::<T>() {
|
||||
Some(inner) => Ok(inner), None => Err(self),
|
||||
Some(inner) => Ok(inner),
|
||||
None => Err(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Downcast for T where T: ErrorTrait + 'static {
|
||||
impl<T> Downcast for T
|
||||
where
|
||||
T: ErrorTrait + 'static,
|
||||
{
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,31 +17,52 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Child> {
|
||||
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<Child> {
|
||||
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()))?,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,34 +17,37 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{fmt::{Formatter, Debug}, collections::HashMap};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{Debug, Formatter},
|
||||
};
|
||||
|
||||
pub struct ExecutionArgs {
|
||||
bind: Vec<String>,
|
||||
dev: Vec<String>,
|
||||
env: Vec<String>,
|
||||
dbus: Vec<String>,
|
||||
vars: HashMap<String, String>,
|
||||
vars: HashMap<String, String>,
|
||||
}
|
||||
|
||||
//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<String>) {
|
||||
pub fn dir(&mut self, dest: impl Into<String>) {
|
||||
self.bind.push("--dir".into());
|
||||
self.bind.push(dest.into());
|
||||
}
|
||||
|
||||
pub fn bind(&mut self, src: impl Into<String>, dest: impl Into<String>) {
|
||||
pub fn bind(&mut self, src: impl Into<String>, dest: impl Into<String>) {
|
||||
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<String>) {
|
||||
self.env.push(src.into());
|
||||
pub fn push_env(&mut self, src: impl Into<String>) {
|
||||
self.env.push(src.into());
|
||||
}
|
||||
|
||||
pub fn get_bind(&self) -> &Vec<String> {
|
||||
&self.bind
|
||||
pub fn get_bind(&self) -> &Vec<String> {
|
||||
&self.bind
|
||||
}
|
||||
|
||||
pub fn get_dev(&self) -> &Vec<String> {
|
||||
&self.dev
|
||||
pub fn get_dev(&self) -> &Vec<String> {
|
||||
&self.dev
|
||||
}
|
||||
|
||||
pub fn get_env(&self) -> &Vec<String> {
|
||||
pub fn get_env(&self) -> &Vec<String> {
|
||||
&self.env
|
||||
}
|
||||
|
||||
pub fn get_dbus(&self) -> &Vec<String> {
|
||||
&self.dbus
|
||||
pub fn get_dbus(&self) -> &Vec<String> {
|
||||
&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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<FilterType> {
|
||||
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<FilterType> {
|
|||
}
|
||||
|
||||
// Generate berkley packet filtering program to pass into the namespaces container
|
||||
pub fn provide_bpf_program(types: Vec<FilterType>, reader: &PipeReader, mut writer: PipeWriter) -> Result<i32, Box<dyn std::error::Error>> {
|
||||
pub fn provide_bpf_program(
|
||||
types: Vec<FilterType>,
|
||||
reader: &PipeReader,
|
||||
mut writer: PipeWriter,
|
||||
) -> Result<i32, Box<dyn std::error::Error>> {
|
||||
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::<Vec<(&str, Action)>>();
|
||||
let rules_cond = RULES_COND.iter()
|
||||
.filter(|a| types.contains(&a.0))
|
||||
.map(|a| (a.1, a.2, a.3))
|
||||
.collect::<Vec<(&str, Action, Compare)>>();
|
||||
let rules = RULES
|
||||
.iter()
|
||||
.filter(|a| types.contains(&a.0))
|
||||
.map(|a| (a.1, a.2))
|
||||
.collect::<Vec<(&str, Action)>>();
|
||||
let rules_cond = RULES_COND
|
||||
.iter()
|
||||
.filter(|a| types.contains(&a.0))
|
||||
.map(|a| (a.1, a.2, a.3))
|
||||
.collect::<Vec<(&str, Action, Compare)>>();
|
||||
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
filter.add_arch(ScmpArch::X86)?;
|
||||
|
@ -133,10 +145,10 @@ pub fn provide_bpf_program(types: Vec<FilterType>, 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();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,52 +17,61 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<i32> {
|
||||
pub fn bwrap_json(mut reader: PipeReader, writer: PipeWriter) -> Result<i32> {
|
||||
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::<Value>(&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<i32> {
|
||||
serialize(params, writer)?;
|
||||
pub fn agent_params(
|
||||
reader: &PipeReader,
|
||||
writer: &PipeWriter,
|
||||
params: &TransactionParameters,
|
||||
metadata: &TransactionMetadata,
|
||||
) -> Result<i32> {
|
||||
serialize(params, writer)?;
|
||||
serialize(&*CONFIG, writer)?;
|
||||
serialize(&*DEFAULT_ALPM_CONF, writer)?;
|
||||
serialize(metadata, writer)?;
|
||||
Ok(reader.as_raw_fd())
|
||||
}
|
||||
|
||||
fn serialize<T: for<'de> Serialize>(input: &T, file: &PipeWriter) -> Result<()> {
|
||||
fn serialize<T: for<'de> 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<Child, std
|
|||
}
|
||||
}
|
||||
|
||||
pub fn wait_on_process(name: &'static str, mut child: Child) -> 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())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,17 +17,17 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,23 +17,19 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Self> {
|
||||
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<String> + 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<String> + 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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<String>, 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::<Vec<_>>()[1].split("\n").collect::<Vec<&str>>()[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)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<ProgressBar>,
|
||||
total_bar: Option<ProgressBar>,
|
||||
condensed: bool,
|
||||
progress: MultiProgress,
|
||||
bars: HashMap<&'static str, ProgressBar>,
|
||||
style: Option<ProgressStyle>,
|
||||
style: Option<ProgressStyle>,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<String>, 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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,27 +17,33 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Arc<str>, (FileType, Arc<str>)>
|
||||
files: IndexMap<Arc<str>, (FileType, Arc<str>)>,
|
||||
}
|
||||
|
||||
impl FileSystemState {
|
||||
|
@ -73,7 +79,7 @@ impl From<i8> 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<Arc<str>, FileSystemState>,
|
||||
state_map: HashMap<Arc<str>, FileSystemState>,
|
||||
state_map_prev: HashMap<Arc<str>, FileSystemState>,
|
||||
linked: HashSet<Arc<str>>,
|
||||
queued: HashSet<&'a str>,
|
||||
progress: ProgressBar,
|
||||
cache: &'a InstanceCache<'a>,
|
||||
pool: Option<ThreadPool>,
|
||||
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<SyncMessage>, Receiver<SyncMessage>) = 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<SyncMessage>, write_chan: &(Sender<()>, Receiver<()>)) {
|
||||
fn wait(&mut self, mut queue: HashSet<&'a str>, rx: Receiver<SyncMessage>, 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<str>) -> 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<str>) -> 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<str>) {
|
||||
|
@ -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<SyncMessage>) -> Result<(), Error> {
|
||||
let instance: Arc<str> = 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<SyncMessage>) -> Result<(), Error> {
|
||||
let mut map = Vec::new();
|
||||
let mut map = Vec::new();
|
||||
let mut prev = Vec::new();
|
||||
let instance: Arc<str> = inshandle.vars().instance().into();
|
||||
let root: Arc<str> = 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<str>, FileSystemSt
|
|||
|
||||
fn obtain_state(root: Arc<str>, 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<str> = entry.path().to_str().unwrap().into();
|
||||
let src_tr: Arc<str> = 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<str>, 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<Metadata,String> {
|
||||
fn metadata(path: &Path) -> Result<Metadata, String> {
|
||||
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(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<String>,
|
||||
handle: &'a Alpm,
|
||||
depth: isize,
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a>DependencyResolver<'a> {
|
||||
impl<'a> DependencyResolver<'a> {
|
||||
pub fn new(alpm: &'a Alpm, ignorelist: &'a HashSet<String>) -> 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<String>>, Vec<Package<'a>>), 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::<Vec<&str>>());
|
||||
})
|
||||
.collect::<Vec<&str>>(),
|
||||
);
|
||||
|
||||
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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<String>, 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<Vec<Package<'a>>, 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::<Vec<&str>>());
|
||||
synchronize.extend(pkg.depends().iter().map(|pkg| pkg.name()).collect::<Vec<&str>>());
|
||||
|
||||
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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* 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<T> = crate::Result<T>;
|
||||
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<Self> where Self: Sized;
|
||||
fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result<TransactionState>;
|
||||
fn new(new: TransactionState, ag: &TransactionAggregator) -> Box<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
fn engage(
|
||||
&self,
|
||||
ag: &mut TransactionAggregator,
|
||||
handle: &mut TransactionHandle,
|
||||
inshandle: &InstanceHandle,
|
||||
) -> Result<TransactionState>;
|
||||
}
|
||||
|
||||
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<String>,
|
||||
foreign_pkgs: HashSet<String>,
|
||||
resident_pkgs: HashSet<String>,
|
||||
queue: Vec<Cow<'a, str>>,
|
||||
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<dyn Transaction> {
|
||||
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::<Vec<_>>(),
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
if queue {
|
||||
self.meta.queue.extend(foreign_pkgs.iter()
|
||||
.map(|p| p.to_owned().into())
|
||||
.collect::<Vec<_>>());
|
||||
if queue {
|
||||
let to_queue = foreign_pkgs.iter().map(|p| p.to_owned().into()).collect::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
let queue = self.meta.queue.iter().map(|i| i.as_ref()).collect::<Vec<_>>();
|
||||
|
||||
if let TransactionMode::Local = self.meta.mode {
|
||||
let upstream = queue.iter()
|
||||
.map(|a| *a)
|
||||
.filter(|a| ignored.contains(*a))
|
||||
.collect::<Vec<&str>>();
|
||||
let upstream = queue.iter().map(|a| *a).filter(|a| ignored.contains(*a)).collect::<Vec<&str>>();
|
||||
|
||||
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::<Vec<&str>>();
|
||||
|
||||
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();
|
||||
}
|
||||
},
|
||||
Transactio |