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();
|
||||
}
|
||||
},
|
||||
TransactionType::Upgrade(..) => {
|
||||
let not_available = queue.iter()
|
||||
.map(|a| *a)
|
||||
.filter(|a| alpm.get_package(a).is_none())
|
||||
.collect::<Vec<&str>>();
|
||||
}
|
||||
TransactionType::Upgrade(..) => {
|
||||
let not_available = queue.iter().map(|a| *a).filter(|a| alpm.get_package(a).is_none()).collect::<Vec<&str>>();
|
||||
|
||||
if ! not_available.is_empty() {
|
||||
if !not_available.is_empty() {
|
||||
err!(SyncError::TargetNotAvailable(not_available[0].into()))?
|
||||
}
|
||||
|
||||
|
@ -379,7 +379,7 @@ impl <'a>TransactionHandle<'a> {
|
|||
continue;
|
||||
}
|
||||
|
||||
alpm.trans_add_pkg(pkg).unwrap();
|
||||
alpm.trans_add_pkg(pkg).unwrap();
|
||||
}
|
||||
|
||||
self.deps = packages.0;
|
||||
|
@ -392,36 +392,40 @@ impl <'a>TransactionHandle<'a> {
|
|||
fn apply_configuration(&mut self, instance: &InstanceHandle, create: bool) -> Result<()> {
|
||||
let depends = instance.metadata().dependencies();
|
||||
let explicit_packages: Vec<&str> = instance.metadata().explicit_packages();
|
||||
let pkgs = self.alpm
|
||||
let pkgs = self
|
||||
.alpm
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.localdb()
|
||||
.pkgs()
|
||||
.iter()
|
||||
.filter(|p| p.reason() == PackageReason::Explicit
|
||||
&& ! p.name().starts_with("pacwrap-")
|
||||
&& ! self.meta.foreign_pkgs.contains(p.name()))
|
||||
.filter(|p| {
|
||||
p.reason() == PackageReason::Explicit
|
||||
&& !p.name().starts_with("pacwrap-")
|
||||
&& !self.meta.foreign_pkgs.contains(p.name())
|
||||
})
|
||||
.map(|p| p.name())
|
||||
.collect();
|
||||
.collect();
|
||||
|
||||
if pkgs != explicit_packages || create {
|
||||
let mut instance = instance.clone();
|
||||
|
||||
instance.metadata_mut().set(depends, pkgs);
|
||||
instance.save()?;
|
||||
instance.save()?;
|
||||
drop(instance);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn trans_ready(&mut self, trans_type: &TransactionType) -> Result<()> {
|
||||
pub fn trans_ready(&mut self, trans_type: &TransactionType) -> Result<()> {
|
||||
if match trans_type {
|
||||
TransactionType::Upgrade(..) => self.alpm().trans_add().len(),
|
||||
TransactionType::Remove(..) => self.alpm().trans_remove().len()
|
||||
} > 0 {
|
||||
TransactionType::Remove(..) => self.alpm().trans_remove().len(),
|
||||
} > 0
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
} else {
|
||||
err!(SyncError::NothingToDo(self.fail))
|
||||
}
|
||||
}
|
||||
|
@ -437,20 +441,20 @@ impl <'a>TransactionHandle<'a> {
|
|||
pub fn release(self) {
|
||||
drop(self);
|
||||
}
|
||||
|
||||
fn set_mode(&mut self, modeset: TransactionMode) {
|
||||
self.meta.mode = modeset;
|
||||
|
||||
fn set_mode(&mut self, modeset: TransactionMode) {
|
||||
self.meta.mode = modeset;
|
||||
}
|
||||
|
||||
pub fn get_mode(&self) -> &TransactionMode {
|
||||
&self.meta.mode
|
||||
pub fn get_mode(&self) -> &TransactionMode {
|
||||
&self.meta.mode
|
||||
}
|
||||
|
||||
pub fn alpm_mut(&mut self) -> &mut Alpm {
|
||||
|
||||
pub fn alpm_mut(&mut self) -> &mut Alpm {
|
||||
self.alpm.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn alpm(&self) -> &Alpm {
|
||||
|
||||
pub fn alpm(&self) -> &Alpm {
|
||||
self.alpm.as_ref().unwrap()
|
||||
}
|
||||
|
||||
|
@ -459,7 +463,7 @@ impl <'a>TransactionHandle<'a> {
|
|||
}
|
||||
|
||||
pub fn set_flags(&mut self, flags: &TransactionFlags, flags_alpm: TransFlag) {
|
||||
self.meta.flags = (flags.bits(), flags_alpm.bits());
|
||||
self.meta.flags = (flags.bits(), flags_alpm.bits());
|
||||
}
|
||||
|
||||
pub fn retrieve_flags(&self) -> (Option<TransactionFlags>, Option<TransFlag>) {
|
||||
|
|
|
@ -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,21 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{err,
|
||||
ErrorKind,
|
||||
error::*,
|
||||
use crate::{
|
||||
config::{cache::InstanceCache, InstanceHandle, InstanceType, CONFIG},
|
||||
constants::{ARROW_GREEN, UNIX_TIMESTAMP},
|
||||
config::InstanceType,
|
||||
exec::utils::execute_fakeroot_container,
|
||||
log::Logger,
|
||||
sync::{self,
|
||||
SyncError,
|
||||
filesystem::FileSystemStateSync,
|
||||
transaction::{Transaction,
|
||||
TransactionHandle,
|
||||
TransactionState,
|
||||
TransactionType,
|
||||
TransactionFlags,
|
||||
TransactionMetadata}},
|
||||
config::{InstanceHandle, InstanceType::ROOT,cache::InstanceCache, CONFIG}};
|
||||
err,
|
||||
error::*,
|
||||
exec::utils::execute_fakeroot_container,
|
||||
log::Logger,
|
||||
sync::{
|
||||
self,
|
||||
filesystem::FileSystemStateSync,
|
||||
transaction::{Transaction, TransactionFlags, TransactionHandle, TransactionMetadata, TransactionState, TransactionType},
|
||||
SyncError,
|
||||
},
|
||||
ErrorKind,
|
||||
};
|
||||
|
||||
pub struct TransactionAggregator<'a> {
|
||||
queried: Vec<&'a str>,
|
||||
|
@ -50,23 +48,25 @@ pub struct TransactionAggregator<'a> {
|
|||
target: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl <'a>TransactionAggregator<'a> {
|
||||
pub fn new(inscache: &'a InstanceCache,
|
||||
queue: HashMap<&'a str, Vec<&'a str>>,
|
||||
log: &'a mut Logger,
|
||||
action_flags: TransactionFlags,
|
||||
action_type: TransactionType,
|
||||
current_target: Option<&'a str>) -> Self {
|
||||
impl<'a> TransactionAggregator<'a> {
|
||||
pub fn new(
|
||||
inscache: &'a InstanceCache,
|
||||
queue: HashMap<&'a str, Vec<&'a str>>,
|
||||
log: &'a mut Logger,
|
||||
action_flags: TransactionFlags,
|
||||
action_type: TransactionType,
|
||||
current_target: Option<&'a str>,
|
||||
) -> Self {
|
||||
Self {
|
||||
queried: Vec::new(),
|
||||
updated: Vec::new(),
|
||||
pkg_queue: queue,
|
||||
filesystem_state: Some(FileSystemStateSync::new(inscache)),
|
||||
action: action_type,
|
||||
action: action_type,
|
||||
cache: inscache,
|
||||
keyring: false,
|
||||
logger: log,
|
||||
flags: action_flags,
|
||||
flags: action_flags,
|
||||
target: current_target,
|
||||
}
|
||||
}
|
||||
|
@ -76,46 +76,48 @@ impl <'a>TransactionAggregator<'a> {
|
|||
let upgrade = match self.action {
|
||||
TransactionType::Upgrade(upgrade, refresh, force) => {
|
||||
if refresh {
|
||||
sync::synchronize_database(self.cache, force)?;
|
||||
sync::synchronize_database(self.cache, force)?;
|
||||
}
|
||||
|
||||
upgrade
|
||||
},
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let target = match self.target {
|
||||
Some(s) => self.cache.get_instance_option(s), None => None
|
||||
Some(s) => self.cache.get_instance_option(s),
|
||||
None => None,
|
||||
};
|
||||
let downstream = self.cache.filter(vec![InstanceType::Aggregate]);
|
||||
let upstream = self.cache.filter(vec![InstanceType::Base, InstanceType::Slice]);
|
||||
let containers = (upstream, downstream);
|
||||
|
||||
if let Some(inshandle) = target {
|
||||
if let InstanceType::BASE | InstanceType::DEP = inshandle.metadata().container_type() {
|
||||
self.transact(inshandle)?;
|
||||
if let Some(ins) = target {
|
||||
if let InstanceType::Base | InstanceType::Slice = ins.metadata().container_type() {
|
||||
self.transact(ins)?;
|
||||
}
|
||||
} else if upgrade {
|
||||
self.transaction(self.cache.registered_base())?;
|
||||
self.transaction(self.cache.registered_dep())?;
|
||||
self.transaction(&containers.0)?;
|
||||
}
|
||||
|
||||
if self.flags.intersects(TransactionFlags::FILESYSTEM_SYNC | TransactionFlags::CREATE)
|
||||
|| self.updated.len() > 0 {
|
||||
if self.cache.registered_root().len() > 0 {
|
||||
if self.flags.intersects(TransactionFlags::FILESYSTEM_SYNC | TransactionFlags::CREATE) || self.updated.len() > 0 {
|
||||
if containers.1.len() > 0 {
|
||||
let file = self.cache.registered();
|
||||
let linker = self.fs_sync().unwrap();
|
||||
|
||||
linker.prepare(file.len());
|
||||
linker.engage(file)?;
|
||||
linker.engage(&file)?;
|
||||
linker.finish();
|
||||
}
|
||||
|
||||
self.filesystem_state = self.filesystem_state.unwrap().release();
|
||||
|
||||
self.filesystem_state = self.filesystem_state.unwrap().release();
|
||||
}
|
||||
|
||||
if let Some(inshandle) = target {
|
||||
if let InstanceType::ROOT = inshandle.metadata().container_type() {
|
||||
self.transact(inshandle)?;
|
||||
if let Some(ins) = target {
|
||||
if let InstanceType::Aggregate = ins.metadata().container_type() {
|
||||
self.transact(ins)?;
|
||||
}
|
||||
} else if upgrade {
|
||||
self.transaction(self.cache.registered_root())?;
|
||||
self.transaction(&containers.1)?;
|
||||
}
|
||||
|
||||
println!("{} Transaction complete.", *ARROW_GREEN);
|
||||
|
@ -123,12 +125,15 @@ impl <'a>TransactionAggregator<'a> {
|
|||
}
|
||||
|
||||
pub fn transaction(&mut self, containers: &Vec<&'a str>) -> Result<()> {
|
||||
for ins in containers.iter() {
|
||||
for ins in containers.iter() {
|
||||
if self.queried.contains(ins) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let inshandle = self.cache.get_instance(ins).unwrap();
|
||||
let inshandle = match self.cache.get_instance_option(ins) {
|
||||
Some(ins) => ins,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
self.queried.push(ins);
|
||||
self.transaction(&inshandle.metadata().dependencies())?;
|
||||
|
@ -138,15 +143,16 @@ impl <'a>TransactionAggregator<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn transact(&mut self, inshandle: &'a InstanceHandle) -> Result<()> {
|
||||
pub fn transact(&mut self, inshandle: &'a InstanceHandle) -> Result<()> {
|
||||
let queue = match self.pkg_queue.get(inshandle.vars().instance()) {
|
||||
Some(some) => some.clone(), None => Vec::new(),
|
||||
Some(some) => some.clone(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
let alpm = sync::instantiate_alpm(&inshandle);
|
||||
let mut meta = TransactionMetadata::new(queue);
|
||||
let mut handle = TransactionHandle::new(&*CONFIG, alpm, &mut meta);
|
||||
let mut act: Box<dyn Transaction> = TransactionState::Prepare.from(self);
|
||||
|
||||
|
||||
self.action.begin_message(&inshandle);
|
||||
|
||||
loop {
|
||||
|
@ -158,15 +164,16 @@ impl <'a>TransactionAggregator<'a> {
|
|||
}
|
||||
|
||||
handle.release();
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
result
|
||||
},
|
||||
}
|
||||
Err(result) => {
|
||||
let is_okay = match result.downcast::<SyncError>() {
|
||||
Ok(error) => match error {
|
||||
SyncError::NothingToDo(bool) => *bool, _ => true,
|
||||
let is_okay = match result.downcast::<SyncError>() {
|
||||
Ok(error) => match error {
|
||||
SyncError::NothingToDo(bool) => *bool,
|
||||
_ => true,
|
||||
},
|
||||
Err(_) => true,
|
||||
};
|
||||
|
@ -177,22 +184,22 @@ impl <'a>TransactionAggregator<'a> {
|
|||
false => return Ok(()),
|
||||
true => return Err(result),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
act = result.from(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyring_update(&mut self, inshandle: &InstanceHandle) -> Result<()> {
|
||||
execute_fakeroot_container(inshandle, vec!("/usr/bin/pacman-key", "--populate", "archlinux"))?;
|
||||
execute_fakeroot_container(inshandle, vec!("/usr/bin/pacman-key", "--updatedb"))?;
|
||||
execute_fakeroot_container(inshandle, vec!["/usr/bin/pacman-key", "--populate", "archlinux"])?;
|
||||
execute_fakeroot_container(inshandle, vec!["/usr/bin/pacman-key", "--updatedb"])?;
|
||||
self.keyring = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sync_filesystem(&mut self, inshandle: &'a InstanceHandle) -> Result<()> {
|
||||
if let ROOT = inshandle.metadata().container_type() {
|
||||
pub fn sync_filesystem(&mut self, inshandle: &'a InstanceHandle) -> Result<()> {
|
||||
if let InstanceType::Aggregate = inshandle.metadata().container_type() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -202,28 +209,28 @@ impl <'a>TransactionAggregator<'a> {
|
|||
fs_sync.engage(&vec![inshandle.vars().instance()])
|
||||
}
|
||||
|
||||
pub fn cache(&self) -> &InstanceCache {
|
||||
pub fn cache(&self) -> &InstanceCache {
|
||||
&self.cache
|
||||
}
|
||||
|
||||
pub fn action(&self) -> &TransactionType {
|
||||
&self.action
|
||||
|
||||
pub fn action(&self) -> &TransactionType {
|
||||
&self.action
|
||||
}
|
||||
|
||||
pub fn deps_updated(&self, inshandle: &InstanceHandle<'a>) -> bool {
|
||||
pub fn deps_updated(&self, inshandle: &InstanceHandle<'a>) -> bool {
|
||||
for ins in inshandle.metadata().dependencies() {
|
||||
if self.updated.contains(&ins) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_keyring_synced(&self) -> bool {
|
||||
self.keyring
|
||||
pub fn is_keyring_synced(&self) -> bool {
|
||||
self.keyring
|
||||
}
|
||||
|
||||
|
||||
pub fn flags(&self) -> &TransactionFlags {
|
||||
&self.flags
|
||||
}
|
||||
|
@ -232,7 +239,7 @@ impl <'a>TransactionAggregator<'a> {
|
|||
&mut self.logger
|
||||
}
|
||||
|
||||
pub fn fs_sync(&mut self) -> Result<&mut FileSystemStateSync<'a>> {
|
||||
pub fn fs_sync(&mut self) -> Result<&mut FileSystemStateSync<'a>> {
|
||||
match self.filesystem_state.as_mut() {
|
||||
Some(linker) => Ok(linker),
|
||||
None => err!(ErrorKind::LinkerUninitialized),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,57 +17,66 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::result::Result as StdResult;
|
||||
use alpm::Alpm;
|
||||
use dialoguer::console::Term;
|
||||
use simplebyteunit::simplebyteunit::{SI, ToByteUnit};
|
||||
use simplebyteunit::simplebyteunit::{ToByteUnit, SI};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::{err,
|
||||
use crate::{
|
||||
config::InstanceHandle,
|
||||
constants::{BOLD, DIM, RESET},
|
||||
err,
|
||||
exec::transaction_agent,
|
||||
sync::{
|
||||
self,
|
||||
transaction::{
|
||||
Transaction,
|
||||
TransactionAggregator,
|
||||
TransactionFlags,
|
||||
TransactionHandle,
|
||||
TransactionParameters,
|
||||
TransactionState::{self, *},
|
||||
TransactionType::{self, *},
|
||||
},
|
||||
utils::erroneous_preparation,
|
||||
SyncError,
|
||||
},
|
||||
utils::prompt::prompt,
|
||||
Error,
|
||||
Result,
|
||||
exec::transaction_agent,
|
||||
sync::{self,
|
||||
SyncError,
|
||||
transaction::{Transaction,
|
||||
TransactionState,
|
||||
TransactionType,
|
||||
TransactionHandle,
|
||||
TransactionAggregator,
|
||||
TransactionFlags,
|
||||
TransactionParameters},
|
||||
utils::erroneous_preparation},
|
||||
utils::prompt::prompt,
|
||||
constants::{RESET, BOLD, DIM},
|
||||
config::InstanceHandle};
|
||||
|
||||
};
|
||||
|
||||
pub struct Commit {
|
||||
state: TransactionState,
|
||||
keyring: bool,
|
||||
}
|
||||
|
||||
impl Transaction for Commit {
|
||||
impl Transaction for Commit {
|
||||
fn new(new: TransactionState, _: &TransactionAggregator) -> Box<Self> {
|
||||
let kr = match new {
|
||||
TransactionState::Commit(bool) => bool, _ => false
|
||||
let kr = match new {
|
||||
Commit(bool) => bool,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
Box::new(Self {
|
||||
state: new,
|
||||
keyring: kr,
|
||||
})
|
||||
Box::new(Self { state: new, keyring: kr })
|
||||
}
|
||||
|
||||
fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result<TransactionState> {
|
||||
fn engage(
|
||||
&self,
|
||||
ag: &mut TransactionAggregator,
|
||||
handle: &mut TransactionHandle,
|
||||
inshandle: &InstanceHandle,
|
||||
) -> Result<TransactionState> {
|
||||
let instance = inshandle.vars().instance();
|
||||
let ready = handle.trans_ready(&ag.action());
|
||||
let state = self.state.as_str();
|
||||
|
||||
if let Err(_) = ready {
|
||||
match ready_state(handle, ag.action(), &self.state) {
|
||||
Some(result) => return result, None => ready?,
|
||||
}
|
||||
}
|
||||
match ready_state(handle, ag.action(), &self.state) {
|
||||
Some(result) => return result,
|
||||
None => ready?,
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(error) = handle.alpm_mut().trans_prepare() {
|
||||
erroneous_preparation(error)?
|
||||
|
@ -75,12 +84,13 @@ impl Transaction for Commit {
|
|||
|
||||
let result = confirm(&self.state, ag, handle);
|
||||
let result = match result {
|
||||
Err(result) => return result, Ok(result) => result
|
||||
Err(result) => return result,
|
||||
Ok(result) => result,
|
||||
};
|
||||
|
||||
handle.set_alpm(None);
|
||||
handle.set_alpm(None);
|
||||
|
||||
let parameters = &TransactionParameters::new(*ag.action(), *handle.get_mode(), result);
|
||||
let parameters = &TransactionParameters::new(*ag.action(), *handle.get_mode(), result);
|
||||
let mut agent = transaction_agent(inshandle, parameters, handle.metadata())?;
|
||||
|
||||
match agent.wait() {
|
||||
|
@ -90,84 +100,100 @@ impl Transaction for Commit {
|
|||
ag.keyring_update(inshandle)?;
|
||||
}
|
||||
|
||||
handle.set_alpm(Some(sync::instantiate_alpm(inshandle)));
|
||||
handle.apply_configuration(inshandle, ag.flags().intersects(TransactionFlags::CREATE))?;
|
||||
handle.set_alpm(Some(sync::instantiate_alpm(inshandle)));
|
||||
handle.apply_configuration(inshandle, ag.flags().intersects(TransactionFlags::CREATE))?;
|
||||
ag.logger().log(format!("container {instance}'s {state} transaction complete")).ok();
|
||||
next_state(handle, ag.action(), &self.state, true)
|
||||
},
|
||||
}
|
||||
1 => err!(SyncError::TransactionFailureAgent),
|
||||
2 => err!(SyncError::ParameterAcquisitionFailure),
|
||||
3 => err!(SyncError::DeserializationFailure),
|
||||
3 => err!(SyncError::DeserializationFailure),
|
||||
4 => err!(SyncError::InvalidMagicNumber),
|
||||
5 => err!(SyncError::AgentVersionMismatch),
|
||||
_ => err!(SyncError::TransactionFailure(format!("Generic failure of agent: Exit code {}", exit_status.code().unwrap_or(-1))))?,
|
||||
_ => err!(SyncError::TransactionFailure(format!(
|
||||
"Generic failure of agent: Exit code {}",
|
||||
exit_status.code().unwrap_or(-1)
|
||||
)))?,
|
||||
},
|
||||
Err(error) => err!(SyncError::TransactionFailure(format!("Execution of agent failed: {}", error)))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn confirm(state: &TransactionState, ag: &TransactionAggregator, handle: &mut TransactionHandle) -> StdResult<(u64, u64), Result<TransactionState>> {
|
||||
fn confirm(
|
||||
state: &TransactionState,
|
||||
ag: &TransactionAggregator,
|
||||
handle: &mut TransactionHandle,
|
||||
) -> StdResult<(u64, u64), Result<TransactionState>> {
|
||||
let sum = summary(handle.alpm());
|
||||
|
||||
if ! handle.get_mode().bool() || ag.flags().intersects(TransactionFlags::DATABASE_ONLY | TransactionFlags::FORCE_DATABASE) {
|
||||
if !handle.get_mode().bool() || ag.flags().intersects(TransactionFlags::DATABASE_ONLY | TransactionFlags::FORCE_DATABASE) {
|
||||
println!("{}", sum.0);
|
||||
|
||||
if ag.flags().contains(TransactionFlags::PREVIEW) {
|
||||
Err(next_state(handle, ag.action(), state, false))?
|
||||
}
|
||||
Err(next_state(handle, ag.action(), state, false))?
|
||||
}
|
||||
|
||||
if ! ag.flags().contains(TransactionFlags::NO_CONFIRM) {
|
||||
if !ag.flags().contains(TransactionFlags::NO_CONFIRM) {
|
||||
let action = ag.action().as_str();
|
||||
let query = format!("Proceed with {action}?");
|
||||
|
||||
if let Err(_) = prompt("::", format!("{}{query}{}", *BOLD, *RESET), true) {
|
||||
Err(next_state(handle, ag.action(), state, false))?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle.alpm_mut().trans_release().ok();
|
||||
Ok(sum.1)
|
||||
}
|
||||
|
||||
fn next_state<'a>(handle: &mut TransactionHandle, action: &TransactionType, state: &TransactionState, updated: bool) -> Result<TransactionState> {
|
||||
fn next_state<'a>(
|
||||
handle: &mut TransactionHandle,
|
||||
action: &TransactionType,
|
||||
state: &TransactionState,
|
||||
updated: bool,
|
||||
) -> Result<TransactionState> {
|
||||
handle.alpm_mut().trans_release().ok();
|
||||
|
||||
|
||||
match action {
|
||||
TransactionType::Remove(..) => Ok(match state {
|
||||
TransactionState::CommitForeign => TransactionState::Complete(updated),
|
||||
TransactionState::Commit(_) => TransactionState::PrepareForeign,
|
||||
_ => unreachable!()
|
||||
Remove(..) => Ok(match state {
|
||||
CommitForeign => Complete(updated),
|
||||
Commit(_) => PrepareForeign(updated),
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
TransactionType::Upgrade(..) => Ok(match state {
|
||||
TransactionState::Commit(_) => TransactionState::Complete(updated),
|
||||
TransactionState::CommitForeign => TransactionState::Stage,
|
||||
_ => unreachable!()
|
||||
Upgrade(..) => Ok(match state {
|
||||
Commit(_) => Complete(updated),
|
||||
CommitForeign => Stage,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn ready_state<'a>(handle: &mut TransactionHandle, action: &TransactionType, state: &TransactionState) -> Option<Result<TransactionState>> {
|
||||
fn ready_state<'a>(
|
||||
handle: &mut TransactionHandle,
|
||||
action: &TransactionType,
|
||||
state: &TransactionState,
|
||||
) -> Option<Result<TransactionState>> {
|
||||
handle.alpm_mut().trans_release().ok();
|
||||
|
||||
match action {
|
||||
TransactionType::Remove(..) => match state {
|
||||
TransactionState::CommitForeign => None,
|
||||
TransactionState::Commit(_) => Some(Ok(TransactionState::PrepareForeign)),
|
||||
_ => unreachable!()
|
||||
|
||||
match action {
|
||||
Remove(..) => match state {
|
||||
CommitForeign => None,
|
||||
Commit(_) => Some(Ok(PrepareForeign(false))),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
TransactionType::Upgrade(..) => match state {
|
||||
TransactionState::Commit(_) => None,
|
||||
TransactionState::CommitForeign => Some(Ok(TransactionState::Stage)),
|
||||
_ => unreachable!()
|
||||
Upgrade(..) => match state {
|
||||
Commit(_) => None,
|
||||
CommitForeign => Some(Ok(Stage)),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn summary(handle: &Alpm) -> (String, (u64, u64)) {
|
||||
fn summary(handle: &Alpm) -> (String, (u64, u64)) {
|
||||
let mut installed_size: i64 = 0;
|
||||
let mut installed_size_old: i64 = 0;
|
||||
let mut installed_size_old: i64 = 0;
|
||||
let mut download: i64 = 0;
|
||||
let mut files_to_download: u64 = 0;
|
||||
let mut current_line_len: usize = 0;
|
||||
|
@ -175,44 +201,49 @@ fn summary(handle: &Alpm) -> (String, (u64, u64)) {
|
|||
let packages = if remove { handle.trans_remove() } else { handle.trans_add() };
|
||||
let size = Term::size(&Term::stdout());
|
||||
let preface = format!("Packages ({}) ", packages.len());
|
||||
let preface_newline = " ".repeat(preface.len());
|
||||
let preface_newline = " ".repeat(preface.len());
|
||||
let line_delimiter = size.1 as usize - preface.len();
|
||||
let mut pkglist: String = String::new();
|
||||
let mut pkglist: String = String::new();
|
||||
let mut summary = format!("\n{}{preface}{}", *BOLD, *RESET);
|
||||
|
||||
for pkg_sync in packages {
|
||||
for pkg_sync in packages {
|
||||
let pkg = match handle.localdb().pkg(pkg_sync.name()) {
|
||||
Ok(pkg) => pkg, Err(_) => pkg_sync,
|
||||
Ok(pkg) => pkg,
|
||||
Err(_) => pkg_sync,
|
||||
};
|
||||
let output = format!("{}-{}{}{} ", pkg.name(), *DIM, pkg_sync.version(), *RESET);
|
||||
let output = format!("{}-{}{}{} ", pkg.name(), *DIM, pkg_sync.version(), *RESET);
|
||||
let download_size = pkg_sync.download_size();
|
||||
let string_len = pkg.name().len() + pkg_sync.version().len() + 2;
|
||||
|
||||
if current_line_len+string_len >= line_delimiter {
|
||||
if current_line_len + string_len >= line_delimiter {
|
||||
summary.push_str(&format!("{pkglist}\n"));
|
||||
pkglist = preface_newline.clone();
|
||||
current_line_len = pkglist.len();
|
||||
current_line_len = pkglist.len();
|
||||
}
|
||||
|
||||
current_line_len += string_len;
|
||||
installed_size_old += pkg.isize();
|
||||
installed_size_old += pkg.isize();
|
||||
installed_size += pkg_sync.isize();
|
||||
|
||||
|
||||
if download_size > 0 {
|
||||
download += download_size;
|
||||
files_to_download += 1;
|
||||
}
|
||||
|
||||
pkglist.push_str(&output);
|
||||
pkglist.push_str(&output);
|
||||
}
|
||||
|
||||
let total_str = if remove { "Total Removed Size" } else { "Total Installed Size" };
|
||||
let net = installed_size-installed_size_old;
|
||||
let total_str = if remove {
|
||||
"Total Removed Size"
|
||||
} else {
|
||||
"Total Installed Size"
|
||||
};
|
||||
let net = installed_size - installed_size_old;
|
||||
|
||||
summary.push_str(&format!("{pkglist}\n\n{}{total_str}{}: {}\n", *BOLD, *RESET, installed_size.to_byteunit(SI)));
|
||||
|
||||
summary.push_str(&format!("{pkglist}\n\n{}{total_str}{}: {}\n", *BOLD, *RESET, installed_size.to_byteunit(SI)));
|
||||
|
||||
if net != 0 {
|
||||
summary.push_str(&format!("{}Net Upgrade Size{}: {}\n", *BOLD, *RESET, net.to_byteunit(SI)));
|
||||
summary.push_str(&format!("{}Net Upgrade Size{}: {}\n", *BOLD, *RESET, net.to_byteunit(SI)));
|
||||
}
|
||||
|
||||
if download > 0 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -16,35 +16,45 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::{err,
|
||||
Error,
|
||||
Result,
|
||||
config::{InstanceHandle, InstanceType},
|
||||
sync::{self,
|
||||
SyncError,
|
||||
transaction::{Transaction,
|
||||
TransactionState,
|
||||
TransactionMode,
|
||||
TransactionType,
|
||||
TransactionHandle,
|
||||
use crate::{
|
||||
config::{InstanceHandle, InstanceType},
|
||||
constants::UNIX_TIMESTAMP,
|
||||
err,
|
||||
sync::{
|
||||
self,
|
||||
transaction::{
|
||||
SyncReqResult,
|
||||
Transaction,
|
||||
TransactionAggregator,
|
||||
TransactionFlags,
|
||||
SyncReqResult}}, constants::UNIX_TIMESTAMP};
|
||||
TransactionHandle,
|
||||
TransactionMode,
|
||||
TransactionState::{self, *},
|
||||
TransactionType::*,
|
||||
},
|
||||
SyncError,
|
||||
},
|
||||
Error,
|
||||
Result,
|
||||
};
|
||||
|
||||
pub struct Prepare {
|
||||
state: TransactionState,
|
||||
}
|
||||
|
||||
impl Transaction for Prepare {
|
||||
impl Transaction for Prepare {
|
||||
fn new(new_state: TransactionState, _: &TransactionAggregator) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
state: new_state,
|
||||
})
|
||||
Box::new(Self { state: new_state })
|
||||
}
|
||||
|
||||
fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result<TransactionState> {
|
||||
fn engage(
|
||||
&self,
|
||||
ag: &mut TransactionAggregator,
|
||||
handle: &mut TransactionHandle,
|
||||
inshandle: &InstanceHandle,
|
||||
) -> Result<TransactionState> {
|
||||
match self.state {
|
||||
TransactionState::Prepare => {
|
||||
Prepare => {
|
||||
let deps: Vec<&str> = inshandle.metadata().dependencies();
|
||||
let instype = inshandle.metadata().container_type();
|
||||
let action = ag.action();
|
||||
|
@ -52,61 +62,64 @@ impl Transaction for Prepare {
|
|||
if deps.len() > 0 {
|
||||
for dep in deps.iter().rev() {
|
||||
match ag.cache().get_instance_option(dep) {
|
||||
Some(dep_handle) => {
|
||||
Some(dep_handle) => {
|
||||
let dep_handle = &sync::instantiate_alpm(dep_handle);
|
||||
let create = ag.flags().contains(TransactionFlags::CREATE);
|
||||
let timestamp = inshandle.metadata().timestamp();
|
||||
let present = *UNIX_TIMESTAMP;
|
||||
|
||||
handle.enumerate_package_lists(dep_handle, create && present == timestamp)
|
||||
},
|
||||
}
|
||||
None => err!(SyncError::DependentContainerMissing(dep.to_string()))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let TransactionType::Upgrade(upgrade, ..) = action {
|
||||
if ! upgrade && handle.metadata().queue.len() == 0 {
|
||||
if let Upgrade(upgrade, ..) = action {
|
||||
if !upgrade && handle.metadata().queue.len() == 0 {
|
||||
err!(SyncError::NothingToDo(true))?
|
||||
}
|
||||
} else {
|
||||
if handle.metadata().queue.len() == 0 {
|
||||
err!(SyncError::NothingToDo(true))?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if handle.metadata().queue.len() == 0 {
|
||||
if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Local) {
|
||||
return Ok(TransactionState::UpToDate)
|
||||
if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Local) {
|
||||
return Ok(UpToDate);
|
||||
}
|
||||
}
|
||||
|
||||
if let TransactionType::Remove(..) = action {
|
||||
Ok(TransactionState::Stage)
|
||||
} else if let InstanceType::BASE = instype {
|
||||
Ok(TransactionState::Stage)
|
||||
if let Remove(..) = action {
|
||||
Ok(Stage)
|
||||
} else if let InstanceType::Base = instype {
|
||||
Ok(Stage)
|
||||
} else {
|
||||
Ok(TransactionState::PrepareForeign)
|
||||
Ok(PrepareForeign(false))
|
||||
}
|
||||
},
|
||||
TransactionState::PrepareForeign => {
|
||||
if let InstanceType::BASE = inshandle.metadata().container_type() {
|
||||
return Ok(TransactionState::Complete(false))
|
||||
}
|
||||
PrepareForeign(updated) => {
|
||||
if let InstanceType::Base = inshandle.metadata().container_type() {
|
||||
return Ok(Complete(false));
|
||||
}
|
||||
|
||||
if ! ag.flags().contains(TransactionFlags::FORCE_DATABASE) {
|
||||
if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Foreign) {
|
||||
if !ag.flags().contains(TransactionFlags::FORCE_DATABASE) {
|
||||
if let SyncReqResult::NotRequired = handle.is_sync_req(TransactionMode::Foreign) {
|
||||
if ag.deps_updated(inshandle) {
|
||||
return Ok(TransactionState::StageForeign)
|
||||
return Ok(StageForeign);
|
||||
}
|
||||
|
||||
return Ok(TransactionState::Stage)
|
||||
return match ag.action() {
|
||||
Remove(..) => Ok(Complete(updated)),
|
||||
Upgrade(..) => Ok(Stage),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TransactionState::StageForeign)
|
||||
Ok(StageForeign)
|
||||
}
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -18,17 +18,24 @@
|
|||
*/
|
||||
use alpm::TransFlag;
|
||||
|
||||
use crate::{err,
|
||||
Error, Result,
|
||||
config::{InstanceType, InstanceHandle},
|
||||
sync::{SyncError,
|
||||
transaction::{Transaction,
|
||||
TransactionState,
|
||||
TransactionMode,
|
||||
TransactionType,
|
||||
TransactionHandle,
|
||||
use crate::{
|
||||
config::{InstanceHandle, InstanceType},
|
||||
err,
|
||||
sync::{
|
||||
transaction::{
|
||||
Transaction,
|
||||
TransactionAggregator,
|
||||
TransactionFlags}}};
|
||||
TransactionFlags,
|
||||
TransactionHandle,
|
||||
TransactionMode::{self, *},
|
||||
TransactionState::{self, *},
|
||||
TransactionType::*,
|
||||
},
|
||||
SyncError,
|
||||
},
|
||||
Error,
|
||||
Result,
|
||||
};
|
||||
|
||||
pub struct Stage {
|
||||
state: TransactionState,
|
||||
|
@ -36,31 +43,36 @@ pub struct Stage {
|
|||
flags: TransFlag,
|
||||
}
|
||||
|
||||
impl Transaction for Stage {
|
||||
fn new(new: TransactionState, ag: &TransactionAggregator) -> Box<Self> {
|
||||
impl Transaction for Stage {
|
||||
fn new(new: TransactionState, ag: &TransactionAggregator) -> Box<Self> {
|
||||
let mut flag;
|
||||
let modeset;
|
||||
|
||||
if let TransactionState::Stage = new {
|
||||
modeset = TransactionMode::Local;
|
||||
modeset = Local;
|
||||
flag = TransFlag::NO_DEP_VERSION;
|
||||
|
||||
|
||||
if ag.flags().contains(TransactionFlags::DATABASE_ONLY) {
|
||||
flag = flag | TransFlag::DB_ONLY;
|
||||
}
|
||||
} else {
|
||||
modeset = TransactionMode::Foreign;
|
||||
modeset = Foreign;
|
||||
flag = TransFlag::NO_DEP_VERSION | TransFlag::DB_ONLY;
|
||||
}
|
||||
|
||||
Box::new(Self {
|
||||
Box::new(Self {
|
||||
state: new,
|
||||
flags: flag,
|
||||
mode: modeset
|
||||
mode: modeset,
|
||||
})
|
||||
}
|
||||
|
||||
fn engage(&self, ag: &mut TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result<TransactionState> {
|
||||
fn engage(
|
||||
&self,
|
||||
ag: &mut TransactionAggregator,
|
||||
handle: &mut TransactionHandle,
|
||||
inshandle: &InstanceHandle,
|
||||
) -> Result<TransactionState> {
|
||||
if let Err(error) = handle.alpm().trans_init(self.flags) {
|
||||
err!(SyncError::InitializationFailure(error.to_string().into()))?
|
||||
}
|
||||
|
@ -71,43 +83,44 @@ impl Transaction for Stage {
|
|||
handle.set_flags(ag.flags(), self.flags);
|
||||
|
||||
match ag.action() {
|
||||
TransactionType::Upgrade(upgrade, downgrade, _) => {
|
||||
Upgrade(upgrade, downgrade, _) => {
|
||||
if *upgrade {
|
||||
handle.alpm().sync_sysupgrade(*downgrade).unwrap();
|
||||
}
|
||||
|
||||
handle.prepare(ag.action(), ag.flags())?;
|
||||
handle.prepare(ag.action(), ag.flags())?;
|
||||
next_state(&self.state, check_keyring(ag, handle, inshandle))
|
||||
},
|
||||
TransactionType::Remove(_,_,_) => {
|
||||
}
|
||||
Remove(..) => {
|
||||
handle.prepare(ag.action(), ag.flags())?;
|
||||
next_state(&self.state, false)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_keyring(ag: &TransactionAggregator, handle: &mut TransactionHandle, inshandle: &InstanceHandle) -> bool {
|
||||
match inshandle.metadata().container_type() {
|
||||
InstanceType::BASE => {
|
||||
InstanceType::Base => {
|
||||
if ag.is_keyring_synced() {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
handle.alpm()
|
||||
|
||||
handle
|
||||
.alpm()
|
||||
.trans_add()
|
||||
.iter()
|
||||
.find_map(|a| Some(a.name() == "archlinux-keyring"))
|
||||
.unwrap_or(false)
|
||||
},
|
||||
_ => false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_state(state: &TransactionState, option: bool) -> Result<TransactionState> {
|
||||
Ok(match state {
|
||||
TransactionState::Stage => TransactionState::Commit(option),
|
||||
TransactionState::StageForeign => TransactionState::CommitForeign,
|
||||
_ => unreachable!()
|
||||
Stage => Commit(option),
|
||||
StageForeign => CommitForeign,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,30 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::{
|
||||
Result,
|
||||
Transaction,
|
||||
TransactionAggregator,
|
||||
TransactionHandle,
|
||||
TransactionState::{self, Complete},
|
||||
};
|
||||
use crate::{config::InstanceHandle, constants::ARROW_GREEN};
|
||||
use super::{Transaction,
|
||||
TransactionState,
|
||||
TransactionHandle,
|
||||
TransactionAggregator,
|
||||
Result};
|
||||
|
||||
pub struct UpToDate;
|
||||
|
||||
impl Transaction for UpToDate {
|
||||
fn new(_: TransactionState, _: &TransactionAggregator) -> Box<Self> {
|
||||
Box::new(Self {})
|
||||
fn new(_: TransactionState, _: &TransactionAggregator) -> Box<Self> {
|
||||
Box::new(Self {})
|
||||
}
|
||||
|
||||
fn engage(&self, _: &mut TransactionAggregator, _: &mut TransactionHandle, inshandle: &InstanceHandle) -> Result<TransactionState> {
|
||||
fn engage(
|
||||
&self,
|
||||
_: &mut TransactionAggregator,
|
||||
_: &mut TransactionHandle,
|
||||
inshandle: &InstanceHandle,
|
||||
) -> Result<TransactionState> {
|
||||
let instance = inshandle.vars().instance();
|
||||
println!("{} {instance} is up-to-date!", *ARROW_GREEN);
|
||||
Ok(TransactionState::Complete(false))
|
||||
println!("{} {instance} is up-to-date!", *ARROW_GREEN);
|
||||
Ok(Complete(false))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,18 +17,20 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use alpm::{CommitResult, FileConflictType, Package, Alpm, PrepareResult};
|
||||
use alpm::{Alpm, CommitResult, FileConflictType, Package, PrepareResult};
|
||||
|
||||
use crate::{err,
|
||||
use crate::{
|
||||
constants::{BOLD, BOLD_WHITE, RESET},
|
||||
err,
|
||||
sync::SyncError,
|
||||
utils::{print_error, print_warning},
|
||||
Error,
|
||||
Result,
|
||||
sync::SyncError,
|
||||
constants::{BOLD, BOLD_WHITE, RESET},
|
||||
utils::{print_error, print_warning}};
|
||||
};
|
||||
|
||||
pub trait AlpmUtils {
|
||||
fn get_local_package(&self, pkg: &str) -> Option<Package<'_>>;
|
||||
fn get_package(&self, pkg: &str) -> Option<Package<'_>>;
|
||||
fn get_package(&self, pkg: &str) -> Option<Package<'_>>;
|
||||
}
|
||||
|
||||
impl AlpmUtils for Alpm {
|
||||
|
@ -36,45 +38,34 @@ impl AlpmUtils for Alpm {
|
|||
if let Ok(pkg) = self.localdb().pkg(pkg) {
|
||||
return Some(pkg);
|
||||
} else {
|
||||
self.localdb()
|
||||
.pkgs()
|
||||
.iter()
|
||||
.find_map(|f| {
|
||||
if f.provides()
|
||||
.iter()
|
||||
.filter(|d| pkg == d.name())
|
||||
.count() > 0 {
|
||||
self.localdb().pkgs().iter().find_map(|f| {
|
||||
if f.provides().iter().filter(|d| pkg == d.name()).count() > 0 {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_package(&self, pkg: &str) -> Option<Package<'_>> {
|
||||
for sync in self.syncdbs() {
|
||||
for sync in self.syncdbs() {
|
||||
if let Ok(pkg) = sync.pkg(pkg) {
|
||||
return Some(pkg);
|
||||
} else {
|
||||
let package = sync.pkgs()
|
||||
.iter()
|
||||
.find_map(|f| {
|
||||
if f.provides()
|
||||
.iter()
|
||||
.filter(|d| pkg == d.name())
|
||||
.count() > 0 {
|
||||
let package = sync.pkgs().iter().find_map(|f| {
|
||||
if f.provides().iter().filter(|d| pkg == d.name()).count() > 0 {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let None = package {
|
||||
continue;
|
||||
}
|
||||
|
||||
return package
|
||||
return package;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,53 +82,74 @@ pub fn erroneous_transaction<'a>(error: (CommitResult<'a>, alpm::Error)) -> Resu
|
|||
let file = conflict.file();
|
||||
let target = conflict.target();
|
||||
print_warning(format!("{}: '{}' already exists.", target, file));
|
||||
},
|
||||
}
|
||||
FileConflictType::Target => {
|
||||
let file = conflict.file();
|
||||
let target = format!("{}{}{}",*BOLD_WHITE, conflict.target(), *RESET);
|
||||
if let Some(conflicting) = conflict.conflicting_target() {
|
||||
let target = format!("{}{}{}", *BOLD_WHITE, conflict.target(), *RESET);
|
||||
if let Some(conflicting) = conflict.conflicting_target() {
|
||||
let conflicting = format!("{}{conflicting}{}", *BOLD_WHITE, *RESET);
|
||||
print_warning(format!("{conflicting}: '{target}' is owned by {file}"));
|
||||
print_warning(format!("{conflicting}: '{target}' is owned by {file}"));
|
||||
} else {
|
||||
print_warning(format!("{target}: '{file}' is owned by foreign target"));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err!(SyncError::TransactionFailure("Conflict within container filesystem".into()))?
|
||||
},
|
||||
CommitResult::PkgInvalid(p) => {
|
||||
}
|
||||
CommitResult::PkgInvalid(p) =>
|
||||
for pkg in p.iter() {
|
||||
let pkg = format!("{}{pkg}{}", *BOLD_WHITE, *RESET);
|
||||
print_error(format!("Invalid package: {}", pkg));
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
print_error(format!("Invalid package: {}", pkg));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
err!(SyncError::TransactionFailure(error.1.to_string()))
|
||||
}
|
||||
|
||||
pub fn erroneous_preparation<'a>(error: (PrepareResult<'a>, alpm::Error)) -> Result<()> {
|
||||
pub fn erroneous_preparation<'a>(error: (PrepareResult<'a>, alpm::Error)) -> Result<()> {
|
||||
match error.0 {
|
||||
PrepareResult::PkgInvalidArch(list) => {
|
||||
for package in list.iter() {
|
||||
print_error(format!("Invalid architecture {}{}{} for {}{}{}", *BOLD, package.arch().unwrap(), *RESET, *BOLD, package.name(), *RESET));
|
||||
}
|
||||
},
|
||||
PrepareResult::UnsatisfiedDeps(list) => {
|
||||
PrepareResult::PkgInvalidArch(list) =>
|
||||
for package in list.iter() {
|
||||
print_error(format!(
|
||||
"Invalid architecture {}{}{} for {}{}{}",
|
||||
*BOLD,
|
||||
package.arch().unwrap(),
|
||||
*RESET,
|
||||
*BOLD,
|
||||
package.name(),
|
||||
*RESET
|
||||
));
|
||||
},
|
||||
PrepareResult::UnsatisfiedDeps(list) =>
|
||||
for missing in list.iter() {
|
||||
print_error(format!("Unsatisifed dependency {}{}{} for target {}{}{}", *BOLD, missing.depend(), *RESET, *BOLD, missing.target(), *RESET));
|
||||
}
|
||||
},
|
||||
PrepareResult::ConflictingDeps(list) => {
|
||||
print_error(format!(
|
||||
"Unsatisifed dependency {}{}{} for target {}{}{}",
|
||||
*BOLD,
|
||||
missing.depend(),
|
||||
*RESET,
|
||||
*BOLD,
|
||||
missing.target(),
|
||||
*RESET
|
||||
));
|
||||
},
|
||||
PrepareResult::ConflictingDeps(list) =>
|
||||
for conflict in list.iter() {
|
||||
print_error(format!("Conflict between {}{}{} and {}{}{}: {}", *BOLD, conflict.package1(), *RESET, *BOLD, conflict.package2(), *RESET, conflict.reason()));
|
||||
}
|
||||
},
|
||||
print_error(format!(
|
||||
"Conflict between {}{}{} and {}{}{}: {}",
|
||||
*BOLD,
|
||||
conflict.package1(),
|
||||
*RESET,
|
||||
*BOLD,
|
||||
conflict.package2(),
|
||||
*RESET,
|
||||
conflict.reason()
|
||||
));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
||||
err!(SyncError::PreparationFailure(error.1.to_string()))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,41 +17,56 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{path::Path, env::var, os::unix::net::UnixStream, fmt::Display, time::{SystemTime, UNIX_EPOCH}};
|
||||
use std::{
|
||||
env::var,
|
||||
fmt::Display,
|
||||
os::unix::net::UnixStream,
|
||||
path::Path,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use nix::unistd::isatty;
|
||||
|
||||
use crate::{err, Error, ErrorKind, Result, constants::{BOLD_RED, BOLD_YELLOW, RESET, TERM, COLORTERM, UID, GID}};
|
||||
use crate::{
|
||||
constants::{BOLD_RED, BOLD_YELLOW, COLORTERM, GID, RESET, TERM, UID},
|
||||
err,
|
||||
Error,
|
||||
ErrorKind,
|
||||
Result,
|
||||
};
|
||||
|
||||
pub use arguments::Arguments;
|
||||
pub use termcontrol::TermControl;
|
||||
|
||||
pub mod termcontrol;
|
||||
pub mod arguments;
|
||||
pub mod prompt;
|
||||
pub mod termcontrol;
|
||||
|
||||
pub fn print_warning(message: impl Into<String> + Display) {
|
||||
eprintln!("{}warning:{} {}", *BOLD_YELLOW, *RESET, &message);
|
||||
}
|
||||
eprintln!("{}warning:{} {}", *BOLD_YELLOW, *RESET, &message);
|
||||
}
|
||||
|
||||
pub fn print_error(message: impl Into<String> + Display) {
|
||||
eprintln!("{}error:{} {}", *BOLD_RED, *RESET, &message);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn env_var(env: &'static str) -> Result<String> {
|
||||
match var(env) {
|
||||
Ok(var) => Ok(var),
|
||||
Err(_) => err!(ErrorKind::EnvVarUnset(env))
|
||||
Err(_) => err!(ErrorKind::EnvVarUnset(env)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_socket(socket: &String) -> bool {
|
||||
match UnixStream::connect(&Path::new(socket)) { Ok(_) => true, Err(_) => false, }
|
||||
match UnixStream::connect(&Path::new(socket)) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_color_terminal() -> bool {
|
||||
let value = *TERM;
|
||||
let is_dumb = ! value.is_empty() && value.to_lowercase() != "dumb";
|
||||
let is_dumb = !value.is_empty() && value.to_lowercase() != "dumb";
|
||||
|
||||
is_dumb && isatty(0).is_ok() && isatty(1).is_ok()
|
||||
}
|
||||
|
@ -60,18 +75,14 @@ pub fn is_truecolor_terminal() -> bool {
|
|||
let value = COLORTERM.to_lowercase();
|
||||
|
||||
is_color_terminal() && value == "truecolor" || value == "24bit"
|
||||
|
||||
}
|
||||
|
||||
pub fn unix_time_as_seconds() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
|
||||
}
|
||||
|
||||
pub fn read_le_32(vec: &Vec<u8>, pos: usize) -> u32 {
|
||||
((vec[pos+0] as u32) << 0) + ((vec[pos+1] as u32) << 8) + ((vec[pos+2] as u32) << 16) + ((vec[pos+3] as u32) << 24)
|
||||
((vec[pos + 0] as u32) << 0) + ((vec[pos + 1] as u32) << 8) + ((vec[pos + 2] as u32) << 16) + ((vec[pos + 3] as u32) << 24)
|
||||
}
|
||||
|
||||
pub fn check_root() -> Result<()> {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::env;
|
||||
use std::{
|
||||
env,
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
|
||||
use crate::{error::*, impl_error, err};
|
||||
use crate::{err, error::*, impl_error};
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Operand<'a> {
|
||||
|
@ -10,7 +12,7 @@ pub enum Operand<'a> {
|
|||
Long(&'a str),
|
||||
LongPos(&'a str, &'a str),
|
||||
Value(&'a str),
|
||||
None
|
||||
Nothing,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -33,9 +35,9 @@ impl_error!(InvalidArgument);
|
|||
|
||||
impl Display for InvalidArgument {
|
||||
fn fmt(&self, fmter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
match self {
|
||||
Self::UnsuppliedOperand(params, message) => write!(fmter, "Option '{params}': {message}"),
|
||||
Self::InvalidOperand(oper) => write!(fmter, "Invalid option '{oper}'"),
|
||||
Self::InvalidOperand(oper) => write!(fmter, "Invalid option '{oper}'"),
|
||||
Self::OperationUnspecified => write!(fmter, "Operation not specified."),
|
||||
Self::TargetUnspecified => write!(fmter, "Target not specified."),
|
||||
}?;
|
||||
|
@ -48,32 +50,35 @@ impl<'a> Arguments<'a> {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
values: env::args()
|
||||
.skip(1)
|
||||
.map(|a| { let a: &str = a.leak(); a })
|
||||
.collect::<Vec<_>>(),
|
||||
.skip(1)
|
||||
.map(|a| {
|
||||
let a: &str = a.leak();
|
||||
a
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
operands: Vec::new(),
|
||||
idx: 0,
|
||||
cur: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn populate(mut self) -> Arguments<'a> {
|
||||
for string in &self.values {
|
||||
match string {
|
||||
string if string.starts_with("--") => {
|
||||
pub fn populate(mut self) -> Arguments<'a> {
|
||||
for string in &self.values {
|
||||
match string {
|
||||
string if string.starts_with("--") =>
|
||||
if string.contains('=') {
|
||||
let value: Vec<&'a str> = string[2..].splitn(2, '=').collect();
|
||||
let value: Vec<&'a str> = string[2 ..].splitn(2, '=').collect();
|
||||
|
||||
self.operands.extend([Operand::Long(value[0]), Operand::LongPos(value[0], value[1])]);
|
||||
self.operands.extend([Operand::Long(value[0]), Operand::LongPos(value[0], value[1])]);
|
||||
} else {
|
||||
self.operands.push(Operand::Long(&string[2..]));
|
||||
}
|
||||
},
|
||||
string if string.starts_with("-") => if string.len() > 1 {
|
||||
for operand in string[1..].chars() {
|
||||
self.operands.push(Operand::Short(operand));
|
||||
}
|
||||
},
|
||||
self.operands.push(Operand::Long(&string[2 ..]));
|
||||
},
|
||||
string if string.starts_with("-") =>
|
||||
if string.len() > 1 {
|
||||
for operand in string[1 ..].chars() {
|
||||
self.operands.push(Operand::Short(operand));
|
||||
}
|
||||
},
|
||||
_ => self.operands.push(match self.operands.last() {
|
||||
Some(last) => match last {
|
||||
Operand::Short(c) => Operand::ShortPos(*c, string),
|
||||
|
@ -90,15 +95,13 @@ impl<'a> Arguments<'a> {
|
|||
|
||||
pub fn target(&mut self) -> Result<&'a str> {
|
||||
for op in self.into_iter() {
|
||||
if let Operand::ShortPos(_, name)
|
||||
| Operand::LongPos(_, name)
|
||||
| Operand::Value(name) = op {
|
||||
if let Operand::ShortPos(_, name) | Operand::LongPos(_, name) | Operand::Value(name) = op {
|
||||
return Ok(name);
|
||||
}
|
||||
}
|
||||
|
||||
err!(InvalidArgument::TargetUnspecified)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_index(&mut self, index: usize) {
|
||||
self.idx = index;
|
||||
|
@ -116,8 +119,8 @@ impl<'a> Arguments<'a> {
|
|||
&self.values
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a>Iterator for Arguments<'a> {
|
||||
|
||||
impl<'a> Iterator for Arguments<'a> {
|
||||
type Item = Operand<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -126,35 +129,34 @@ impl <'a>Iterator for Arguments<'a> {
|
|||
if self.cur < self.operands.len() {
|
||||
self.idx += 1;
|
||||
Some(self.operands[self.cur])
|
||||
} else {
|
||||
} else {
|
||||
self.set_index(0);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a>Default for &Operand<'a> {
|
||||
impl<'a> Default for &Operand<'a> {
|
||||
fn default() -> Self {
|
||||
&Operand::None
|
||||
&Operand::Nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a>Default for Operand<'a> {
|
||||
impl<'a> Default for Operand<'a> {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
Self::Nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a>Display for Operand<'a> {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl<'a> Display for Operand<'a> {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Operand::Long(str) => write!(fmt, "--{}", str),
|
||||
Operand::LongPos(str, eq) => write!(fmt, "--{}={}", str, eq),
|
||||
Operand::Short(char) => write!(fmt, "-{}", char),
|
||||
Operand::ShortPos(str, eq) => write!(fmt, "-{} {}", str, eq),
|
||||
Operand::Value(str) => write!(fmt, "{}", str),
|
||||
Operand::None => write!(fmt, "None"),
|
||||
Operand::Nothing => write!(fmt, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use dialoguer::{theme::ColorfulTheme, Input, console::{style, Style}};
|
||||
use dialoguer::{
|
||||
console::{style, Style},
|
||||
theme::ColorfulTheme,
|
||||
Input,
|
||||
};
|
||||
|
||||
pub fn prompt(prefix: &str, prompt: impl Into<String>, yn_prompt: bool) -> Result<(),()> {
|
||||
if let Ok(value) = create_prompt(prompt.into(), prefix,
|
||||
if yn_prompt { "[Y/n]" } else { "[N/y]" }) {
|
||||
pub fn prompt(prefix: &str, prompt: impl Into<String>, yn_prompt: bool) -> Result<(), ()> {
|
||||
if let Ok(value) = create_prompt(prompt.into(), prefix, if yn_prompt { "[Y/n]" } else { "[N/y]" }) {
|
||||
if value.to_lowercase() == "y" || (yn_prompt && value.is_empty()) {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -19,14 +22,11 @@ fn create_prompt(message: String, prefix: &str, prompt: &str) -> Result<String,
|
|||
prompt_prefix: style(prefix.into()).blue().bold(),
|
||||
error_prefix: style(prefix.into()).red().bold(),
|
||||
prompt_suffix: style(prompt.to_string()).bold(),
|
||||
success_suffix: style(prompt.to_string()).bold(),
|
||||
success_suffix: style(prompt.to_string()).bold(),
|
||||
prompt_style: Style::new(),
|
||||
values_style: Style::new(),
|
||||
..ColorfulTheme::default()
|
||||
};
|
||||
|
||||
return Input::with_theme(&theme)
|
||||
.with_prompt(message)
|
||||
.allow_empty(true)
|
||||
.interact_text();
|
||||
return Input::with_theme(&theme).with_prompt(message).allow_empty(true).interact_text();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap-core
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,55 +17,51 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use nix::sys::termios::{Termios,
|
||||
tcgetattr,
|
||||
tcsetattr,
|
||||
SetArg::TCSANOW};
|
||||
use nix::sys::termios::{tcgetattr, tcsetattr, SetArg::TCSANOW, Termios};
|
||||
|
||||
use crate::{err, Error, ErrorKind, Result};
|
||||
|
||||
/*******
|
||||
*
|
||||
* ermControl struct
|
||||
*
|
||||
* Impelments basic, portable functionalily for controlling terminal parameters.
|
||||
*
|
||||
***/
|
||||
*
|
||||
* ermControl struct
|
||||
*
|
||||
* Impelments basic, portable functionalily for controlling terminal parameters.
|
||||
*
|
||||
***/
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TermControl {
|
||||
tm: Option<Termios>,
|
||||
fd: i32
|
||||
fd: i32,
|
||||
}
|
||||
|
||||
impl TermControl {
|
||||
/*
|
||||
* A valid termios struct is presumed to be returned
|
||||
* if there is a valid tty at specified fd.
|
||||
* A valid termios struct is presumed to be returned
|
||||
* if there is a valid tty at specified fd.
|
||||
*
|
||||
* If the application is not being instantiated from a tty,
|
||||
* If the application is not being instantiated from a tty,
|
||||
* then return TermControl with tm set with None.
|
||||
*/
|
||||
|
||||
pub fn new(f: i32) -> Self {
|
||||
match tcgetattr(f) {
|
||||
Ok(t) => Self { tm: Some(t), fd: f},
|
||||
Err(_) => Self { tm: None, fd: f}
|
||||
Ok(t) => Self { tm: Some(t), fd: f },
|
||||
Err(_) => Self { tm: None, fd: f },
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if Termios initiated and then execute tcsetattr to reset terminal.
|
||||
*/
|
||||
/*
|
||||
* Check if Termios initiated and then execute tcsetattr to reset terminal.
|
||||
*/
|
||||
|
||||
pub fn reset_terminal(&self) -> Result<()> {
|
||||
pub fn reset_terminal(&self) -> Result<()> {
|
||||
match self.tm.as_ref() {
|
||||
Some(tm) => match tcsetattr(self.fd, TCSANOW, tm) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(errno) => err!(ErrorKind::Termios(errno)),
|
||||
},
|
||||
None => Ok(())
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,32 +17,32 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::process::Command;
|
||||
use std::env::var;
|
||||
use std::{env::var, process::Command};
|
||||
|
||||
fn head() -> String {
|
||||
match Command::new("git").args(["rev-parse", "--short", "HEAD"]).output() {
|
||||
Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()),
|
||||
Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()),
|
||||
Err(_) => "N/A".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn time(debug: bool) -> String {
|
||||
match debug {
|
||||
false => match Command::new("git").args(["log", "-1", "--date=format:%d/%m/%Y", "--format=%ad"]).output() {
|
||||
Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()),
|
||||
Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()),
|
||||
Err(_) => "N/A".into(),
|
||||
},
|
||||
true => match Command::new("date").args(["+%d/%m/%Y %T"]).output() {
|
||||
Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()),
|
||||
Ok(output) => String::from_utf8(output.stdout).unwrap_or("N/A".into()),
|
||||
Err(_) => "N/A".into(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn release(debug: bool) -> &'static str {
|
||||
match debug {
|
||||
true => "DEV", false => "RELEASE",
|
||||
true => "DEV",
|
||||
false => "RELEASE",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ fn is_debug() -> bool {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
if ! cfg!(target_os="linux") || ! cfg!(target_family="unix") {
|
||||
if !cfg!(target_os = "linux") || !cfg!(target_family = "unix") {
|
||||
panic!("Unsupported build target. Please refer to the documentation for further information.")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -19,12 +19,14 @@
|
|||
|
||||
use std::process::Command;
|
||||
|
||||
use pacwrap_core::{config,
|
||||
use pacwrap_core::{
|
||||
config,
|
||||
err,
|
||||
error::*,
|
||||
exec::utils::handle_process,
|
||||
utils::arguments::{Arguments, Operand},
|
||||
ErrorKind,
|
||||
error::*,
|
||||
err};
|
||||
utils::arguments::{Arguments, Operand},
|
||||
ErrorKind,
|
||||
};
|
||||
|
||||
fn save_configuration() -> Result<()> {
|
||||
err!(ErrorKind::Message("This function has been deprecated."))?
|
||||
|
@ -35,10 +37,10 @@ fn print_configuration(instance: &str) -> Result<()> {
|
|||
let mut pkgs_string = String::new();
|
||||
let mut depends_string = String::new();
|
||||
|
||||
println!("INSTANCE_CONFIG[{},0]={}", instance, ins.metadata().container_type());
|
||||
println!("INSTANCE_CONFIG[{},0]={}", instance, ins.metadata().container_type());
|
||||
|
||||
for i in ins.metadata().dependencies() {
|
||||
depends_string.push_str(&format!("{} ", i));
|
||||
depends_string.push_str(&format!("{} ", i));
|
||||
}
|
||||
println!("INSTANCE_CONFIG[{},1]=\"{}\"", instance, depends_string);
|
||||
|
||||
|
@ -54,12 +56,10 @@ pub fn compat(args: &mut Arguments) -> Result<()> {
|
|||
match args.next().unwrap_or_default() {
|
||||
Operand::Short('s') | Operand::Long("save") => save_configuration(),
|
||||
Operand::Short('l') | Operand::Long("load") => print_configuration(args.target()?),
|
||||
_ => args.invalid_operand()
|
||||
_ => args.invalid_operand(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_bash(executable: &'static str, args: &mut Arguments) -> Result<()> {
|
||||
handle_process(&executable, Command::new(&executable)
|
||||
.args(args.values())
|
||||
.spawn())
|
||||
pub fn execute_bash(executable: &'static str, args: &mut Arguments) -> Result<()> {
|
||||
handle_process(&executable, Command::new(&executable).args(args.values()).spawn())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,94 +17,100 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{os::unix::{process::ExitStatusExt, io::AsRawFd},
|
||||
process::{Command, Child, ExitStatus, exit},
|
||||
fs::{File, remove_file},
|
||||
use std::{
|
||||
fs::{remove_file, File},
|
||||
os::unix::{io::AsRawFd, process::ExitStatusExt},
|
||||
path::{Path, PathBuf},
|
||||
process::{exit, Child, Command, ExitStatus},
|
||||
thread,
|
||||
time::Duration,
|
||||
vec::Vec,
|
||||
thread};
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use nix::{unistd::Pid, sys::signal::{Signal, kill}};
|
||||
use signal_hook::{consts::*, iterator::Signals};
|
||||
use command_fds::{CommandFdExt, FdMapping};
|
||||
use nix::{
|
||||
sys::signal::{kill, Signal},
|
||||
unistd::Pid,
|
||||
};
|
||||
use signal_hook::{consts::*, iterator::Signals};
|
||||
|
||||
use pacwrap_core::{err,
|
||||
ErrorKind,
|
||||
error::*,
|
||||
exec::{ExecutionError,
|
||||
args::ExecutionArgs,
|
||||
seccomp::{provide_bpf_program, configure_bpf_program},
|
||||
utils::{bwrap_json, execute_fakeroot_container}},
|
||||
constants::{self,
|
||||
DEFAULT_PATH,
|
||||
BWRAP_EXECUTABLE,
|
||||
DBUS_PROXY_EXECUTABLE,
|
||||
DBUS_SOCKET,
|
||||
XDG_RUNTIME_DIR},
|
||||
config::{self,
|
||||
Dbus,
|
||||
InstanceHandle,
|
||||
use pacwrap_core::{
|
||||
config::{
|
||||
self,
|
||||
register::{register_dbus, register_filesystems, register_permissions},
|
||||
Dbus,
|
||||
InstanceHandle,
|
||||
InstanceType,
|
||||
register::{register_filesystems,
|
||||
register_permissions,
|
||||
register_dbus}},
|
||||
utils::{TermControl,
|
||||
arguments::{Arguments, Operand, InvalidArgument},
|
||||
env_var,
|
||||
print_warning,
|
||||
check_root}};
|
||||
},
|
||||
constants::{self, BWRAP_EXECUTABLE, DBUS_PROXY_EXECUTABLE, DBUS_SOCKET, DEFAULT_PATH, XDG_RUNTIME_DIR},
|
||||
err,
|
||||
error::*,
|
||||
exec::{
|
||||
args::ExecutionArgs,
|
||||
seccomp::{configure_bpf_program, provide_bpf_program},
|
||||
utils::{bwrap_json, execute_fakeroot_container},
|
||||
ExecutionError,
|
||||
},
|
||||
utils::{
|
||||
arguments::{Arguments, InvalidArgument, Operand as Op},
|
||||
check_root,
|
||||
env_var,
|
||||
print_warning,
|
||||
TermControl,
|
||||
},
|
||||
ErrorKind,
|
||||
};
|
||||
|
||||
const PROCESS_SLEEP_DURATION: Duration = Duration::from_millis(250);
|
||||
const SOCKET_SLEEP_DURATION: Duration = Duration::from_micros(500);
|
||||
|
||||
enum ExecParams<'a> {
|
||||
Root(i8, bool, Vec<&'a str>, InstanceHandle<'a>),
|
||||
Root(i8, bool, Vec<&'a str>, InstanceHandle<'a>),
|
||||
Container(i8, bool, Vec<&'a str>, InstanceHandle<'a>),
|
||||
}
|
||||
|
||||
/*
|
||||
* Open an issue or possibly submit a PR if this module's argument parser
|
||||
* Open an issue or possibly submit a PR if this module's argument parser
|
||||
* is conflicting with an application you use.
|
||||
*/
|
||||
|
||||
impl <'a>ExecParams<'a> {
|
||||
impl<'a> ExecParams<'a> {
|
||||
fn parse(args: &'a mut Arguments) -> Result<Self> {
|
||||
let runtime = args.values()
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
let str = *f;
|
||||
|
||||
match str {
|
||||
string if string.starts_with("-E")
|
||||
| string.eq("--exec")
|
||||
| string.eq("--target")
|
||||
| string.eq("--verbose")
|
||||
| string.eq("--shell")
|
||||
| string.eq("--root")
|
||||
| string.eq("--fake-chroot") => None,
|
||||
_ => Some(str),
|
||||
}
|
||||
})
|
||||
.skip(1)
|
||||
.collect();
|
||||
let mut verbosity: i8 = 0;
|
||||
let mut shell = false;
|
||||
let mut root = false;
|
||||
let mut container = None;
|
||||
let runtime = args
|
||||
.values()
|
||||
.iter()
|
||||
.filter_map(|f| match *f {
|
||||
string
|
||||
if string.starts_with("-E")
|
||||
| string.eq("--exec")
|
||||
| string.eq("--target")
|
||||
| string.eq("--verbose")
|
||||
| string.eq("--shell")
|
||||
| string.eq("--root")
|
||||
| string.eq("--fake-chroot") =>
|
||||
None,
|
||||
_ => Some(*f),
|
||||
})
|
||||
.skip(1)
|
||||
.collect();
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::Short('r') | Operand::Long("root") => root = true,
|
||||
Operand::Short('s') | Operand::Long("shell") => shell = true,
|
||||
Operand::Short('v') | Operand::Long("verbose") => verbosity += 1,
|
||||
Operand::ShortPos('E', str)
|
||||
| Operand::ShortPos('s', str)
|
||||
| Operand::ShortPos('r', str)
|
||||
| Operand::ShortPos('v', str)
|
||||
| Operand::LongPos("target", str)
|
||||
| Operand::Value(str) => if let None = container {
|
||||
container = Some(str);
|
||||
Op::Short('r') | Op::Long("root") => root = true,
|
||||
Op::Short('s') | Op::Long("shell") => shell = true,
|
||||
Op::Short('v') | Op::Long("verbose") => verbosity += 1,
|
||||
Op::ShortPos('E', str)
|
||||
| Op::ShortPos('s', str)
|
||||
| Op::ShortPos('r', str)
|
||||
| Op::ShortPos('v', str)
|
||||
| Op::LongPos("target", str)
|
||||
| Op::Value(str) =>
|
||||
if let None = container {
|
||||
container = Some(str);
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
|
@ -115,8 +121,8 @@ impl <'a>ExecParams<'a> {
|
|||
None => err!(InvalidArgument::TargetUnspecified)?,
|
||||
};
|
||||
|
||||
if let InstanceType::DEP = handle.metadata().container_type() {
|
||||
err!(ErrorKind::Message("Execution upon sliced filesystems is not supported."))?
|
||||
if let InstanceType::Slice = handle.metadata().container_type() {
|
||||
err!(ErrorKind::Message("Execution in container filesystem segments is not supported."))?
|
||||
}
|
||||
|
||||
Ok(match root {
|
||||
|
@ -130,9 +136,9 @@ pub fn execute<'a>(args: &'a mut Arguments<'a>) -> Result<()> {
|
|||
check_root()?;
|
||||
|
||||
match ExecParams::parse(args)? {
|
||||
ExecParams::Root(verbosity, true, _, handle) => execute_fakeroot(&handle, vec!("bash"), verbosity),
|
||||
ExecParams::Root(verbosity, false, args, handle) => execute_fakeroot(&handle, args, verbosity),
|
||||
ExecParams::Container(verbosity, true, _, handle) => execute_container(&handle, vec!("bash"), true, verbosity),
|
||||
ExecParams::Root(verbosity, true, _, handle) => execute_fakeroot(&handle, vec!["bash"], verbosity),
|
||||
ExecParams::Root(verbosity, false, args, handle) => execute_fakeroot(&handle, args, verbosity),
|
||||
ExecParams::Container(verbosity, true, _, handle) => execute_container(&handle, vec!["bash"], true, verbosity),
|
||||
ExecParams::Container(verbosity, false, args, handle) => execute_container(&handle, args, false, verbosity),
|
||||
}
|
||||
}
|
||||
|
@ -142,85 +148,105 @@ fn execute_container<'a>(ins: &InstanceHandle, arguments: Vec<&str>, shell: bool
|
|||
let mut jobs: Vec<Child> = Vec::new();
|
||||
let cfg = ins.config();
|
||||
let vars = ins.vars();
|
||||
|
||||
if ! cfg.allow_forking() {
|
||||
exec_args.push_env("--die-with-parent");
|
||||
|
||||
if !cfg.allow_forking() {
|
||||
exec_args.push_env("--die-with-parent");
|
||||
}
|
||||
|
||||
if ! cfg.enable_userns() {
|
||||
exec_args.push_env("--unshare-user");
|
||||
exec_args.push_env("--disable-userns");
|
||||
if !cfg.enable_userns() {
|
||||
exec_args.push_env("--unshare-user");
|
||||
exec_args.push_env("--disable-userns");
|
||||
} else {
|
||||
print_warning("Namespace nesting has been known in the past to enable container escape vulnerabilities.");
|
||||
}
|
||||
|
||||
match ! cfg.retain_session() {
|
||||
match !cfg.retain_session() {
|
||||
true => exec_args.push_env("--new-session"),
|
||||
false => print_warning("Retaining a console session is known to allow for container escape. See CVE-2017-5226 for details."),
|
||||
false =>
|
||||
print_warning("Retaining a console session is known to allow for container escape. See CVE-2017-5226 for details."),
|
||||
}
|
||||
|
||||
match shell && *constants::IS_COLOR_TERMINLAL {
|
||||
match shell && *constants::IS_COLOR_TERMINLAL {
|
||||
true => exec_args.env("TERM", "xterm"),
|
||||
false => exec_args.env("TERM", "dumb"),
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.dbus().len() > 0 {
|
||||
jobs.push(instantiate_dbus_proxy(cfg.dbus(), &mut exec_args)?);
|
||||
if cfg.dbus().len() > 0 {
|
||||
jobs.push(instantiate_dbus_proxy(cfg.dbus(), &mut exec_args)?);
|
||||
}
|
||||
|
||||
exec_args.env("XDG_RUNTIME_DIR", &*XDG_RUNTIME_DIR);
|
||||
register_filesystems(cfg.filesystem(), &vars, &mut exec_args)?;
|
||||
register_permissions(cfg.permissions(), &mut exec_args)?;
|
||||
|
||||
|
||||
let path = match exec_args.get_var("PATH") {
|
||||
Some(var) => var.as_str(), None => { exec_args.env("PATH", DEFAULT_PATH); DEFAULT_PATH },
|
||||
Some(var) => var.as_str(),
|
||||
None => {
|
||||
exec_args.env("PATH", DEFAULT_PATH);
|
||||
DEFAULT_PATH
|
||||
}
|
||||
};
|
||||
let path_vec: Vec<&str> = path.split(":").collect();
|
||||
let (reader, writer) = os_pipe::pipe().unwrap();
|
||||
let fd = writer.as_raw_fd();
|
||||
let (sec_reader, sec_writer) = os_pipe::pipe().unwrap();
|
||||
let sec_fd = match cfg.seccomp() {
|
||||
true => provide_bpf_program(configure_bpf_program(cfg), &sec_reader, sec_writer).unwrap(),
|
||||
false => { print_warning("Disabling seccomp filtering can allow for sandbox escape."); 0 },
|
||||
true => provide_bpf_program(configure_bpf_program(cfg), &sec_reader, sec_writer).unwrap(),
|
||||
false => {
|
||||
print_warning("Disabling seccomp filtering can allow for sandbox escape.");
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let tc = TermControl::new(0);
|
||||
let tc = TermControl::new(0);
|
||||
let mut proc = Command::new(BWRAP_EXECUTABLE);
|
||||
|
||||
proc.arg("--dir")
|
||||
proc.env_clear()
|
||||
.arg("--dir")
|
||||
.arg("/tmp")
|
||||
.args(exec_args.get_bind())
|
||||
.arg("--dev").arg("/dev")
|
||||
.arg("--proc").arg("/proc")
|
||||
.arg("--dev")
|
||||
.arg("/dev")
|
||||
.arg("--proc")
|
||||
.arg("/proc")
|
||||
.args(exec_args.get_dev())
|
||||
.arg("--unshare-all")
|
||||
.arg("--clearenv")
|
||||
.args(exec_args.get_env())
|
||||
.arg("--info-fd")
|
||||
.arg(fd.to_string());
|
||||
|
||||
let fd_mappings = match sec_fd {
|
||||
0 => vec![FdMapping { parent_fd: fd, child_fd: fd }],
|
||||
0 => vec![FdMapping {
|
||||
parent_fd: fd,
|
||||
child_fd: fd,
|
||||
}],
|
||||
_ => {
|
||||
proc.arg("--seccomp")
|
||||
.arg(sec_fd.to_string());
|
||||
vec![FdMapping { parent_fd: fd, child_fd: fd },
|
||||
FdMapping { parent_fd: sec_fd, child_fd: sec_fd }]
|
||||
},
|
||||
proc.arg("--seccomp").arg(sec_fd.to_string());
|
||||
vec![
|
||||
FdMapping {
|
||||
parent_fd: fd,
|
||||
child_fd: fd,
|
||||
},
|
||||
FdMapping {
|
||||
parent_fd: sec_fd,
|
||||
child_fd: sec_fd,
|
||||
},
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if verbosity == 1 {
|
||||
println!("Arguments:\t {arguments:?}\n{ins:?}");
|
||||
eprintln!("Arguments:\t {arguments:?}\n{ins:?}");
|
||||
} else if verbosity > 1 {
|
||||
println!("Arguments:\t {arguments:?}\n{ins:?}\n{exec_args:?}");
|
||||
eprintln!("Arguments:\t {arguments:?}\n{ins:?}\n{exec_args:?}");
|
||||
}
|
||||
|
||||
check_path(ins, &arguments, path_vec)?;
|
||||
proc.args(arguments).fd_mappings(fd_mappings).unwrap();
|
||||
proc.args(arguments).fd_mappings(fd_mappings).unwrap();
|
||||
|
||||
match proc.spawn() {
|
||||
Ok(c) => Ok(wait_on_process(c, bwrap_json(reader, writer)?, *cfg.allow_forking(), jobs, tc))?,
|
||||
Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())),
|
||||
Err(err) => err!(ErrorKind::ProcessInitFailure(BWRAP_EXECUTABLE, err.kind())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,10 +254,10 @@ fn signal_trap(bwrap_pid: i32) {
|
|||
let mut signals = Signals::new(&[SIGHUP, SIGINT, SIGQUIT, SIGTERM]).unwrap();
|
||||
|
||||
thread::spawn(move || {
|
||||
let proc: &str = &format!("/proc/{}/", bwrap_pid);
|
||||
let proc = Path::new(proc);
|
||||
let proc: &str = &format!("/proc/{}/", bwrap_pid);
|
||||
let proc = Path::new(proc);
|
||||
|
||||
for _ in signals.forever() {
|
||||
for _ in signals.forever() {
|
||||
if proc.exists() {
|
||||
kill(Pid::from_raw(bwrap_pid), Signal::SIGKILL).unwrap();
|
||||
}
|
||||
|
@ -239,14 +265,14 @@ fn signal_trap(bwrap_pid: i32) {
|
|||
});
|
||||
}
|
||||
|
||||
fn wait_on_process(mut process: Child, bwrap_pid: i32, block: bool, mut jobs: Vec<Child>, tc: TermControl) -> Result<()> {
|
||||
fn wait_on_process(mut process: Child, bwrap_pid: i32, block: bool, mut jobs: Vec<Child>, tc: TermControl) -> Result<()> {
|
||||
signal_trap(bwrap_pid);
|
||||
|
||||
|
||||
match process.wait() {
|
||||
Ok(status) => {
|
||||
if block {
|
||||
let proc: &str = &format!("/proc/{}/", bwrap_pid);
|
||||
let proc = Path::new(proc);
|
||||
let proc: &str = &format!("/proc/{}/", bwrap_pid);
|
||||
let proc = Path::new(proc);
|
||||
|
||||
while proc.exists() {
|
||||
thread::sleep(PROCESS_SLEEP_DURATION);
|
||||
|
@ -256,14 +282,14 @@ fn wait_on_process(mut process: Child, bwrap_pid: i32, block: bool, mut jobs: Ve
|
|||
for job in jobs.iter_mut() {
|
||||
job.kill().unwrap();
|
||||
}
|
||||
|
||||
|
||||
if let Err(err) = cleanup(&tc) {
|
||||
err.warn();
|
||||
}
|
||||
|
||||
process_exit(status)
|
||||
},
|
||||
Err(error) => err!(ErrorKind::ProcessWaitFailure(BWRAP_EXECUTABLE, error.kind()))
|
||||
}
|
||||
Err(error) => err!(ErrorKind::ProcessWaitFailure(BWRAP_EXECUTABLE, error.kind())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,10 +302,10 @@ fn cleanup(tc: &TermControl) -> Result<()> {
|
|||
fn process_exit(status: ExitStatus) -> Result<()> {
|
||||
exit(match status.code() {
|
||||
Some(status) => status,
|
||||
None => {
|
||||
eprint!("\nbwrap process {status}");
|
||||
println!();
|
||||
100+status.signal().unwrap_or(0)
|
||||
None => {
|
||||
eprint!("\nbwrap process {status}");
|
||||
println!();
|
||||
100 + status.signal().unwrap_or(0)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -292,18 +318,21 @@ fn instantiate_dbus_proxy(per: &Vec<Box<dyn Dbus>>, args: &mut ExecutionArgs) ->
|
|||
let dbus_session = env_var("DBUS_SESSION_BUS_ADDRESS")?;
|
||||
|
||||
match Command::new(DBUS_PROXY_EXECUTABLE)
|
||||
.arg(dbus_session).arg(&*DBUS_SOCKET)
|
||||
.args(args.get_dbus()).spawn() {
|
||||
.arg(dbus_session)
|
||||
.arg(&*DBUS_SOCKET)
|
||||
.args(args.get_dbus())
|
||||
.spawn()
|
||||
{
|
||||
Ok(mut child) => {
|
||||
let mut increment: u8 = 0;
|
||||
|
||||
|
||||
args.robind(&*DBUS_SOCKET, &dbus_socket_path);
|
||||
args.symlink(&dbus_socket_path, "/run/dbus/system_bus_socket");
|
||||
args.env("DBUS_SESSION_BUS_ADDRESS", format!("unix:path={dbus_socket_path}"));
|
||||
|
||||
/*
|
||||
* This blocking code is required to prevent a downstream race condition with
|
||||
* bubblewrap. Unless xdg-dbus-proxy is passed improper parameters, this while loop
|
||||
|
||||
/*
|
||||
* This blocking code is required to prevent a downstream race condition with
|
||||
* bubblewrap. Unless xdg-dbus-proxy is passed improper parameters, this while loop
|
||||
* shouldn't almost ever increment more than once or twice.
|
||||
*
|
||||
* With a sleep duration of 500 microseconds, we check the socket 200 times before failure.
|
||||
|
@ -312,18 +341,18 @@ fn instantiate_dbus_proxy(per: &Vec<Box<dyn Dbus>>, args: &mut ExecutionArgs) ->
|
|||
* to wait on a FD prior to instantiating the filesystem bindings.
|
||||
*/
|
||||
|
||||
while ! check_socket(&*DBUS_SOCKET, &increment, &mut child)? {
|
||||
while !check_socket(&*DBUS_SOCKET, &increment, &mut child)? {
|
||||
increment += 1;
|
||||
}
|
||||
|
||||
Ok(child)
|
||||
},
|
||||
Err(error) => err!(ErrorKind::ProcessInitFailure(DBUS_PROXY_EXECUTABLE, error.kind()))?
|
||||
}
|
||||
Err(error) => err!(ErrorKind::ProcessInitFailure(DBUS_PROXY_EXECUTABLE, error.kind()))?,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_socket(socket: &String, increment: &u8, process_child: &mut Child) -> Result<bool> {
|
||||
if increment == &200 {
|
||||
if increment == &200 {
|
||||
process_child.kill().ok();
|
||||
clean_up_socket(&*DBUS_SOCKET)?;
|
||||
err!(ExecutionError::SocketTimeout(socket.into()))?
|
||||
|
@ -336,11 +365,11 @@ fn check_socket(socket: &String, increment: &u8, process_child: &mut Child) -> R
|
|||
fn create_placeholder(path: &str) -> Result<()> {
|
||||
match File::create(path) {
|
||||
Ok(file) => Ok(drop(file)),
|
||||
Err(error) => err!(ErrorKind::IOError(path.into(), error.kind()))
|
||||
Err(error) => err!(ErrorKind::IOError(path.into(), error.kind())),
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_up_socket(path: &str) -> Result<()> {
|
||||
fn clean_up_socket(path: &str) -> Result<()> {
|
||||
if let Err(error) = remove_file(path) {
|
||||
match error.kind() {
|
||||
std::io::ErrorKind::NotFound => return Ok(()),
|
||||
|
@ -351,12 +380,12 @@ fn clean_up_socket(path: &str) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_fakeroot(ins: &InstanceHandle, arguments: Vec<&str>, verbosity: i8) -> Result<()> {
|
||||
fn execute_fakeroot(ins: &InstanceHandle, arguments: Vec<&str>, verbosity: i8) -> Result<()> {
|
||||
if verbosity > 0 {
|
||||
println!("Arguments:\t {arguments:?}\n{ins:?}\n");
|
||||
eprintln!("Arguments:\t {arguments:?}\n{ins:?}\n");
|
||||
}
|
||||
|
||||
check_path(ins, &arguments, vec!("/usr/bin", "/bin"))?;
|
||||
check_path(ins, &arguments, vec!["/usr/bin", "/bin"])?;
|
||||
execute_fakeroot_container(ins, arguments)
|
||||
}
|
||||
|
||||
|
@ -369,9 +398,12 @@ fn check_path(ins: &InstanceHandle, args: &Vec<&str>, path: Vec<&str>) -> Result
|
|||
let root = ins.vars().root().as_ref();
|
||||
|
||||
for dir in path {
|
||||
match Path::new(&format!("{}/{}",root,dir)).try_exists() {
|
||||
Ok(_) => if dest_exists(root, dir, exec)? { return Ok(()) },
|
||||
Err(error) => err!(ExecutionError::InvalidPathVar(dir.into(), error.kind()))?
|
||||
match Path::new(&format!("{}/{}", root, dir)).try_exists() {
|
||||
Ok(_) =>
|
||||
if dest_exists(root, dir, exec)? {
|
||||
return Ok(());
|
||||
},
|
||||
Err(error) => err!(ExecutionError::InvalidPathVar(dir.into(), error.kind()))?,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,18 +431,18 @@ fn dest_exists(root: &str, dir: &str, exec: &str) -> Result<bool> {
|
|||
} else if let Ok(path) = path_direct.read_link() {
|
||||
if let Some(path) = path.as_os_str().to_str() {
|
||||
return dest_exists(root, dir, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(path.exists() | path_direct.exists())
|
||||
}
|
||||
|
||||
fn obtain_path(path: &Path, exec: &str) -> Result<PathBuf> {
|
||||
match Path::canonicalize(&path) {
|
||||
Ok(path) => Ok(path),
|
||||
Ok(path) => Ok(path),
|
||||
Err(err) => match err.kind() {
|
||||
std::io::ErrorKind::NotFound => Ok(path.to_path_buf()),
|
||||
_ => err!(ErrorKind::IOError(exec.into(), err.kind()))?,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -19,22 +19,22 @@
|
|||
|
||||
use pacwrap_core::utils::arguments::{Arguments, Operand};
|
||||
|
||||
mod sync;
|
||||
mod remove;
|
||||
mod query;
|
||||
mod exec;
|
||||
mod compat;
|
||||
mod exec;
|
||||
mod manual;
|
||||
mod query;
|
||||
mod remove;
|
||||
mod sync;
|
||||
|
||||
fn main() {
|
||||
let arguments = &mut Arguments::new().populate();
|
||||
let result = match arguments.next().unwrap_or_default() {
|
||||
Operand::Short('E') | Operand::Long("exec") => exec::execute(arguments),
|
||||
Operand::Short('S') | Operand::Long("sync") => sync::synchronize(arguments),
|
||||
Operand::Short('S') | Operand::Long("sync") => sync::synchronize(arguments),
|
||||
Operand::Short('R') | Operand::Long("remove") => remove::remove(arguments),
|
||||
Operand::Short('Q') | Operand::Long("query") => query::query(arguments),
|
||||
Operand::Short('U') | Operand::Long("utils") => compat::execute_bash("pacwrap-utils", arguments),
|
||||
Operand::Short('P') | Operand::Long("proc") => compat::execute_bash("pacwrap-ps", arguments),
|
||||
Operand::Short('P') | Operand::Long("proc") => compat::execute_bash("pacwrap-ps", arguments),
|
||||
Operand::Short('h') | Operand::Long("help") => manual::help(arguments),
|
||||
Operand::Short('V') | Operand::Long("version") => manual::print_version(arguments),
|
||||
Operand::Long("compat") => compat::compat(arguments),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -19,26 +19,27 @@
|
|||
|
||||
use indexmap::IndexSet;
|
||||
use lazy_static::lazy_static;
|
||||
use std::fmt::{Write, Formatter, Display};
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
|
||||
use pacwrap_core::{err,
|
||||
use pacwrap_core::{
|
||||
err,
|
||||
impl_error,
|
||||
utils::{arguments::Operand, is_color_terminal, is_truecolor_terminal, Arguments},
|
||||
Error,
|
||||
ErrorTrait,
|
||||
utils::{Arguments,
|
||||
arguments::Operand,
|
||||
is_color_terminal,
|
||||
is_truecolor_terminal}};
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref HELP_ALL: Vec<HelpTopic> =
|
||||
[HelpTopic::Execute,
|
||||
static ref HELP_ALL: Vec<HelpTopic> = [
|
||||
HelpTopic::Execute,
|
||||
HelpTopic::Sync,
|
||||
HelpTopic::Process,
|
||||
HelpTopic::Utils,
|
||||
HelpTopic::Help,
|
||||
HelpTopic::Version,
|
||||
HelpTopic::Copyright].into();
|
||||
HelpTopic::Version,
|
||||
HelpTopic::Copyright
|
||||
]
|
||||
.into();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -63,11 +64,12 @@ pub fn help(mut args: &mut Arguments) -> Result<(), Error> {
|
|||
let mut buffer = String::new();
|
||||
|
||||
for topic in help.0 {
|
||||
topic.write(&mut buffer, help.1).unwrap();
|
||||
topic.write(&mut buffer, help.1).unwrap();
|
||||
}
|
||||
|
||||
match help.1 {
|
||||
HelpLayout::Console => print!("\x1b[?7l{buffer}\x1b[?7h"), _ => print!("{buffer}"),
|
||||
HelpLayout::Console => print!("\x1b[?7l{buffer}\x1b[?7h"),
|
||||
_ => print!("{buffer}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -75,91 +77,64 @@ pub fn help(mut args: &mut Arguments) -> Result<(), Error> {
|
|||
|
||||
fn ascertain_help<'a>(args: &mut Arguments) -> Result<(IndexSet<&'a HelpTopic>, &'a HelpLayout), Error> {
|
||||
let mut layout = match is_color_terminal() {
|
||||
true => &HelpLayout::Console, false => &HelpLayout::Dumb,
|
||||
true => &HelpLayout::Console,
|
||||
false => &HelpLayout::Dumb,
|
||||
};
|
||||
let mut topic: Vec<&HelpTopic> = vec!(&HelpTopic::Default);
|
||||
let mut topic: Vec<&HelpTopic> = vec![&HelpTopic::Default];
|
||||
let mut more = false;
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::Long("format")
|
||||
| Operand::Long("help")
|
||||
| Operand::Short('f')
|
||||
| Operand::Short('h')
|
||||
=> continue,
|
||||
Operand::Short('m')
|
||||
| Operand::Long("more")
|
||||
=> more = true,
|
||||
Operand::LongPos("format", "dumb")
|
||||
| Operand::ShortPos('f', "dumb")
|
||||
=> layout = &HelpLayout::Dumb,
|
||||
Operand::LongPos("format", "markdown")
|
||||
| Operand::ShortPos('f', "markdown")
|
||||
=> layout = &HelpLayout::Markdown,
|
||||
Operand::LongPos("format", "man")
|
||||
| Operand::ShortPos('f', "man")
|
||||
=> layout = &HelpLayout::Man,
|
||||
Operand::LongPos("format", "ansi")
|
||||
| Operand::ShortPos('f', "ansi")
|
||||
=> layout = &HelpLayout::Console,
|
||||
Operand::Long("format") | Operand::Long("help") | Operand::Short('f') | Operand::Short('h') => continue,
|
||||
Operand::Short('m') | Operand::Long("more") => more = true,
|
||||
Operand::ShortPos('f', "man") | Operand::LongPos("format", "man") => layout = &HelpLayout::Man,
|
||||
Operand::ShortPos('f', "ansi") | Operand::LongPos("format", "ansi") => layout = &HelpLayout::Console,
|
||||
Operand::ShortPos('f', "dumb") | Operand::LongPos("format", "dumb") => layout = &HelpLayout::Dumb,
|
||||
Operand::ShortPos('f', "markdown") | Operand::LongPos("format", "markdown") => layout = &HelpLayout::Markdown,
|
||||
Operand::ShortPos('h', "sync")
|
||||
| Operand::ShortPos('h', "S")
|
||||
| Operand::LongPos("help", "sync")
|
||||
| Operand::LongPos("help", "S")
|
||||
=> topic.push(&HelpTopic::Sync),
|
||||
| Operand::ShortPos('h', "S")
|
||||
| Operand::LongPos("help", "sync")
|
||||
| Operand::LongPos("help", "S") => topic.push(&HelpTopic::Sync),
|
||||
Operand::ShortPos('h', "E")
|
||||
| Operand::ShortPos('h', "exec")
|
||||
| Operand::LongPos("help", "E")
|
||||
| Operand::LongPos("help", "exec")
|
||||
=> topic.push(&HelpTopic::Execute),
|
||||
| Operand::ShortPos('h', "exec")
|
||||
| Operand::LongPos("help", "E")
|
||||
| Operand::LongPos("help", "exec") => topic.push(&HelpTopic::Execute),
|
||||
Operand::ShortPos('h', "process")
|
||||
| Operand::ShortPos('h', "P")
|
||||
| Operand::LongPos("help", "process")
|
||||
| Operand::LongPos("help", "P")
|
||||
=> topic.push(&HelpTopic::Process),
|
||||
| Operand::ShortPos('h', "P")
|
||||
| Operand::LongPos("help", "process")
|
||||
| Operand::LongPos("help", "P") => topic.push(&HelpTopic::Process),
|
||||
Operand::ShortPos('h', "utils")
|
||||
| Operand::ShortPos('h', "U")
|
||||
| Operand::LongPos("help", "utils")
|
||||
| Operand::LongPos("help", "U")
|
||||
=> topic.push(&HelpTopic::Utils),
|
||||
| Operand::ShortPos('h', "U")
|
||||
| Operand::LongPos("help", "utils")
|
||||
| Operand::LongPos("help", "U") => topic.push(&HelpTopic::Utils),
|
||||
Operand::ShortPos('h', "help")
|
||||
| Operand::ShortPos('h', "h")
|
||||
| Operand::LongPos("help", "help")
|
||||
| Operand::LongPos("help", "h")
|
||||
=> topic.push(&HelpTopic::Help),
|
||||
Operand::ShortPos('h', "synopsis")
|
||||
| Operand::LongPos("help", "synopsis")
|
||||
=> topic.push(&HelpTopic::Default),
|
||||
| Operand::ShortPos('h', "h")
|
||||
| Operand::LongPos("help", "help")
|
||||
| Operand::LongPos("help", "h") => topic.push(&HelpTopic::Help),
|
||||
Operand::ShortPos('h', "V")
|
||||
| Operand::ShortPos('h', "version")
|
||||
| Operand::LongPos("help", "V")
|
||||
| Operand::LongPos("help", "version")
|
||||
=> topic.push(&HelpTopic::Version),
|
||||
Operand::ShortPos('h', "copyright")
|
||||
| Operand::LongPos("help", "copyright")
|
||||
=> topic.push(&HelpTopic::Copyright),
|
||||
Operand::ShortPos('h', "all")
|
||||
| Operand::LongPos("help", "all")
|
||||
| Operand::Short('a')
|
||||
| Operand::Long("all")
|
||||
=> topic.extend(HELP_ALL.iter()),
|
||||
Operand::ShortPos('h', topic)
|
||||
| Operand::LongPos("help", topic)
|
||||
=> err!(ErrorKind::InvalidTopic(topic.into()))?,
|
||||
_ => args.invalid_operand()?,
|
||||
| Operand::ShortPos('h', "version")
|
||||
| Operand::LongPos("help", "V")
|
||||
| Operand::LongPos("help", "version") => topic.push(&HelpTopic::Version),
|
||||
Operand::ShortPos('h', "copyright") | Operand::LongPos("help", "copyright") => topic.push(&HelpTopic::Copyright),
|
||||
Operand::ShortPos('h', "synopsis") | Operand::LongPos("help", "synopsis") => topic.push(&HelpTopic::Default),
|
||||
Operand::ShortPos('h', "all") | Operand::LongPos("help", "all") | Operand::Short('a') | Operand::Long("all") =>
|
||||
topic.extend(HELP_ALL.iter()),
|
||||
Operand::ShortPos('h', topic) | Operand::LongPos("help", topic) => err!(ErrorKind::InvalidTopic(topic.into()))?,
|
||||
_ => args.invalid_operand()?,
|
||||
}
|
||||
}
|
||||
|
||||
let len = topic.len();
|
||||
let start = if more || len == 1 || len > 7 { 0 } else { 1 };
|
||||
let start = if more || len == 1 || len > 7 { 0 } else { 1 };
|
||||
|
||||
args.set_index(1);
|
||||
Ok((topic.drain(start..).collect(), layout))
|
||||
Ok((topic.drain(start ..).collect(), layout))
|
||||
}
|
||||
|
||||
fn minimal(args: &mut Arguments) -> bool {
|
||||
match args.next().unwrap_or_default() {
|
||||
Operand::LongPos("version", "min") | Operand::ShortPos('V', "min") => true, _ => false
|
||||
match args.next().unwrap_or_default() {
|
||||
Operand::LongPos("version", "min") | Operand::ShortPos('V', "min") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,14 +147,14 @@ enum HelpTopic {
|
|||
Process,
|
||||
Help,
|
||||
Copyright,
|
||||
Version
|
||||
Version,
|
||||
}
|
||||
|
||||
enum HelpLayout {
|
||||
enum HelpLayout {
|
||||
Man,
|
||||
Dumb,
|
||||
Markdown,
|
||||
Console
|
||||
Console,
|
||||
}
|
||||
|
||||
impl HelpLayout {
|
||||
|
@ -196,7 +171,7 @@ impl HelpLayout {
|
|||
match self {
|
||||
Self::Console => " [37;1m",
|
||||
Self::Markdown => "* **",
|
||||
Self::Man => ".TP\n\\fB",
|
||||
Self::Man => ".TP\n\\fB",
|
||||
Self::Dumb => " ",
|
||||
}
|
||||
}
|
||||
|
@ -204,17 +179,15 @@ impl HelpLayout {
|
|||
fn sub_text(&self) -> &str {
|
||||
match self {
|
||||
Self::Console | Self::Dumb => " ",
|
||||
Self::Man => ".PP\n",
|
||||
Self::Markdown => "",
|
||||
Self::Man => ".PP\n",
|
||||
Self::Markdown => "",
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&self) -> &str {
|
||||
match self {
|
||||
Self::Console => "[0m",
|
||||
Self::Markdown
|
||||
| Self::Man
|
||||
| Self::Dumb => ""
|
||||
Self::Markdown | Self::Man | Self::Dumb => "",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +196,7 @@ impl HelpLayout {
|
|||
Self::Console => "[0m",
|
||||
Self::Man => "\\fP",
|
||||
Self::Markdown => "**",
|
||||
Self::Dumb => ""
|
||||
Self::Dumb => "",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,21 +220,21 @@ impl HelpLayout {
|
|||
impl HelpTopic {
|
||||
fn write(&self, buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
Self::Default => default(buf,layout),
|
||||
Self::Sync => sync(buf,layout),
|
||||
Self::Execute => execute(buf,layout),
|
||||
Self::Process => process(buf,layout),
|
||||
Self::Utils => utils(buf,layout),
|
||||
Self::Help => meta(buf,layout),
|
||||
Self::Copyright => copyright(buf,layout),
|
||||
Self::Version => version(buf,layout),
|
||||
Self::Default => default(buf, layout),
|
||||
Self::Sync => sync(buf, layout),
|
||||
Self::Execute => execute(buf, layout),
|
||||
Self::Process => process(buf, layout),
|
||||
Self::Utils => utils(buf, layout),
|
||||
Self::Help => meta(buf, layout),
|
||||
Self::Copyright => copyright(buf, layout),
|
||||
Self::Version => version(buf, layout),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
let head = layout.head();
|
||||
let tab = layout.tab();
|
||||
let tab = layout.tab();
|
||||
let sub = layout.sub();
|
||||
let bold = layout.bold();
|
||||
let reset = layout.reset();
|
||||
|
@ -269,20 +242,28 @@ fn default(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error>
|
|||
let name = env!("CARGO_PKG_NAME");
|
||||
|
||||
match layout {
|
||||
HelpLayout::Man => writeln!(buf, ".nh\n.TH {name} 1 \"{}-{} ({})\" {name} \"User Manual\"\n",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("PACWRAP_BUILDSTAMP"),
|
||||
env!("PACWRAP_BUILDTIME"))?,
|
||||
HelpLayout::Markdown => writeln!(buf, "# Pacwrap User Manual
|
||||
HelpLayout::Man => writeln!(
|
||||
buf,
|
||||
".nh\n.TH {name} 1 \"{}-{} ({})\" {name} \"User Manual\"\n",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("PACWRAP_BUILDSTAMP"),
|
||||
env!("PACWRAP_BUILDTIME")
|
||||
)?,
|
||||
HelpLayout::Markdown => writeln!(
|
||||
buf,
|
||||
"# Pacwrap User Manual
|
||||
|
||||
This document was generated by the {name} binary on {} with version {}-{} of the program.\n",
|
||||
env!("PACWRAP_BUILDTIME"),
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("PACWRAP_BUILDSTAMP"))?,
|
||||
_ => ()
|
||||
This document was generated by the {name} binary on {} with version {}-{} of the program.\n",
|
||||
env!("PACWRAP_BUILDTIME"),
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("PACWRAP_BUILDSTAMP")
|
||||
)?,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
writeln!(buf, "{head}NAME{reset}
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}NAME{reset}
|
||||
{tab}pacwrap - Command-line application which facilitates the creation, management, and execution of unprivileged,
|
||||
{tab}sandboxed containers with bubblewrap and libalpm.
|
||||
|
||||
|
@ -307,7 +288,8 @@ This document was generated by the {name} binary on {} with version {}-{} of the
|
|||
{tab}{tab}Invoke a printout of this manual to {bold}STDOUT{reset_bold}.
|
||||
|
||||
{sub}-V, --version{reset_bold}
|
||||
{tab}{tab}Display version and copyright information in {bold}STDOUT{reset_bold}.\n")
|
||||
{tab}{tab}Display version and copyright information in {bold}STDOUT{reset_bold}.\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn execute(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
|
@ -317,13 +299,16 @@ fn execute(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error>
|
|||
let reset = layout.reset();
|
||||
let reset_bold = layout.reset_bold();
|
||||
|
||||
writeln!(buf, "{head}EXECUTE{reset}
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}EXECUTE{reset}
|
||||
|
||||
{sub}-r, --root{reset_bold}
|
||||
{tab}{tab}Execute operation with fakeroot and fakechroot. Facilitates a command with faked privileges.
|
||||
|
||||
{sub}-s, --shell{reset_bold}
|
||||
{tab}{tab}Invoke a bash shell\n")
|
||||
{tab}{tab}Invoke a bash shell\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
|
@ -334,7 +319,9 @@ fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
|||
let reset_bold = layout.reset_bold();
|
||||
let tab = layout.tab();
|
||||
|
||||
writeln!(buf, "{head}HELP{reset}
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}HELP{reset}
|
||||
|
||||
{sub}-m, --more{reset_bold}
|
||||
{tab}{tab}When specifying a topic to display, show the default topic in addition to specified options.
|
||||
|
@ -345,7 +332,8 @@ fn meta(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
|||
{tab}{tab}outside the context of package maintenance. 'man' option produces troff-formatted documents for man pages.
|
||||
|
||||
{sub}-a, --all, --help=all{reset_bold}
|
||||
{tab}{tab}Display all help topics.\n")
|
||||
{tab}{tab}Display all help topics.\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
|
@ -353,10 +341,12 @@ fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
|||
let bold = layout.bold();
|
||||
let tab = layout.tab();
|
||||
let sub = layout.sub();
|
||||
let reset = layout.reset();
|
||||
let reset = layout.reset();
|
||||
let reset_bold = layout.reset_bold();
|
||||
|
||||
writeln!(buf, "{head}SYNCHRONIZATION{reset}
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}SYNCHRONIZATION{reset}
|
||||
|
||||
{sub}-y, --refresh{reset_bold}
|
||||
{tab}{tab}Synchronize remote package databases. Specify up to 2 times to force a refresh.
|
||||
|
@ -377,29 +367,36 @@ fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
|||
|
||||
{sub}-b, --base{reset_bold}
|
||||
{tab}{tab}Base container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation.
|
||||
{tab}{tab}
|
||||
{tab}{tab}This container type is used as the base layer for all downstream containers. Only one base container
|
||||
{tab}{tab}dependency per slice or per root is supported. Filesystem and package deduplication via slices
|
||||
{tab}{tab}and root containers is recommended, but optional.
|
||||
{tab}{tab}dependency per slice or aggregate is supported. Filesystem and package deduplication via slices and
|
||||
{tab}{tab}aggregate containers are recommended, but optional.
|
||||
|
||||
{sub}-s, --slice{reset_bold}
|
||||
{tab}{tab}Slice container type. Specify alongside {bold}-c, --create{reset_bold} to assign this container type during creation.
|
||||
{tab}{tab}Requires a base dependency be specified, and one or more sliced dependencies, in order to ascertain
|
||||
{tab}{tab}foreign packages and influence ordering of downstream synchronization target(s). Container slicing
|
||||
{tab}{tab}provides the ability to install packages in a lightweight, sliced filesytem, which aid in the
|
||||
{tab}{tab}deduplication of common downstream package and filesystem dependencies e.g. graphics drivers,
|
||||
{tab}{tab}graphical toolkits, fonts, etc..
|
||||
{tab}{tab}
|
||||
{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, to ascertain foreign
|
||||
{tab}{tab}packages and influence ordering of downstream synchronization target(s). Container slicing provies
|
||||
{tab}{tab}the ability to install packages in a lightweight, sliced filesytem, which aid in the deduplication
|
||||
{tab}{tab}of common downstream package and filesystem dependencies.
|
||||
{tab}{tab}
|
||||
{tab}{tab}Useful for graphics drivers, graphical toolkits, fonts, etc.; these are not meant for applications.
|
||||
|
||||
{sub}-r, --root{reset_bold}
|
||||
{tab}{tab}Root container type. Specify alongside {bold}-c, --create{reset_bold} to this assign container type during creation.
|
||||
{tab}{tab}Requires a base dependency be specified, and optionally one or more sliced dependencies, in order
|
||||
{tab}{tab}to ascertain foreign packages and influence ordering of this target. These containers are ideal
|
||||
{tab}{tab}for installing software in with the least amount of filesystem and package synchronization overhead.
|
||||
{sub}-a, --aggegrate{reset_bold}
|
||||
{tab}{tab}Aggregate container type. Specify alongside {bold}-c, --create{reset_bold} to this assign container type during creation.
|
||||
{tab}{tab}
|
||||
{tab}{tab}Requires a base dependency, and optionally one or more sliced dependencies, in order to acertain foreign
|
||||
{tab}{tab}packages and amalgamate the target. These containers are ideal for installing software with the aid of
|
||||
{tab}{tab}filesystem and package deduplication.
|
||||
{tab}{tab}
|
||||
{tab}{tab}Useful for all general purpose applications, browsers, e-mail clients, and even terminal user interface
|
||||
{tab}{tab}applications such as IRC clients. It is recommended to base your containers on aggregate type containers.
|
||||
|
||||
{sub}-t, --target=TARGET{reset_bold}
|
||||
{tab}{tab}Specify a target container for the specified operation.
|
||||
|
||||
{sub}-d, --dep=DEPEND{reset_bold}
|
||||
{tab}{tab}Specify a dependency container for the specified operation.
|
||||
{tab}{tab}Specify a dependent container for the specified operation.
|
||||
|
||||
{sub}-o, --target-only{reset_bold}
|
||||
{tab}{tab}Apply specified operation on the specified target only.
|
||||
|
@ -413,17 +410,20 @@ fn sync(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
|||
{tab}{tab}Transact on resident containers with a database-only transaction.
|
||||
|
||||
{sub}--noconfirm{reset_bold}
|
||||
{tab}{tab}Override confirmation prompts and confirm all operations.\n")
|
||||
|
||||
{tab}{tab}Override confirmation prompts and confirm all operations.\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn process(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
let head = layout.head();
|
||||
let tab = layout.tab();
|
||||
let reset = layout.reset();
|
||||
|
||||
writeln!(buf, "{head}PROCESS{reset}
|
||||
{tab}{tab}-TODO-\n")
|
||||
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}PROCESS{reset}
|
||||
{tab}{tab}-TODO-\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn utils(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
|
@ -431,8 +431,11 @@ fn utils(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
|||
let tab = layout.tab();
|
||||
let reset = layout.reset();
|
||||
|
||||
writeln!(buf, "{head}UTILITIES{reset}
|
||||
{tab}{tab}-TODO-\n")
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}UTILITIES{reset}
|
||||
{tab}{tab}-TODO-\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn version(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
|
@ -449,14 +452,17 @@ fn version(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error>
|
|||
let release = env!("PACWRAP_BUILD");
|
||||
let version_num = env!("CARGO_PKG_VERSION");
|
||||
|
||||
writeln!(buf, "{head}VERSION{reset}
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}VERSION{reset}
|
||||
|
||||
{sub}-V, --version, --version=min{reset_bold}
|
||||
{tab}{tab}Sends version information to {bold}STDOUT{reset_bold} with colourful ASCII art.
|
||||
{tab}{tab}The 'min' option provides a minimalistic output as is provided to non-colour terms.
|
||||
|
||||
{sub_text}This documentation was generated by {name} v{version_num}-{suffix}-{release} ({timestamp}).
|
||||
{tab}Please seek relevant documentation if '{name} -V' mismatches with the aforementioned.\n")
|
||||
{tab}Please seek relevant documentation if '{name} -V' mismatches with the aforementioned.\n"
|
||||
)
|
||||
}
|
||||
|
||||
fn copyright(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Error> {
|
||||
|
@ -464,22 +470,25 @@ fn copyright(buf: &mut String, layout: &HelpLayout) -> Result<(), std::fmt::Erro
|
|||
let tab = layout.tab();
|
||||
let reset = layout.reset();
|
||||
|
||||
writeln!(buf, "{head}COPYRIGHT{reset}
|
||||
writeln!(
|
||||
buf,
|
||||
"{head}COPYRIGHT{reset}
|
||||
|
||||
{tab}{tab}Copyright (C) 2023-2024 Xavier R.M.
|
||||
|
||||
{tab}{tab}This program may be freely redistributed under the
|
||||
{tab}{tab}terms of the GNU General Public License v3 only.\n")
|
||||
{tab}{tab}terms of the GNU General Public License v3 only.\n"
|
||||
)
|
||||
}
|
||||
|
||||
pub fn print_version(mut args: &mut Arguments) -> Result<(), Error> {
|
||||
let name = env!("CARGO_PKG_NAME");
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let name = env!("CARGO_PKG_NAME");
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let suffix = env!("PACWRAP_BUILDSTAMP");
|
||||
let timestamp = env!("PACWRAP_BUILDTIME");
|
||||
let release = env!("PACWRAP_BUILD");
|
||||
|
||||
if ! minimal(&mut args) && is_truecolor_terminal() {
|
||||
if !minimal(&mut args) && is_truecolor_terminal() {
|
||||
println!("\x1b[?7l\n [0m[38;2;8;7;6m [0m[38;2;35;31;23mR[0m[38;2;62;56;41mP[0m[38;2;90;81;58mA[0m[38;2;117;105;76mA[0m[38;2;146;131;94mC[0m[38;2;174;156;111mW[0m[38;2;204;182;130mW[0m[38;2;225;200;142mR[0m[38;2;196;173;120mR[0m[38;2;149;130;91mA[0m[38;2;101;88;62mA[0m[38;2;53;46;33mP[0m[38;2;10;8;6m [0m
|
||||
[0m[38;2;14;12;10m [0m[38;2;40;36;26mR[0m[38;2;67;60;43mA[0m[38;2;93;83;60mP[0m[38;2;120;107;77mP[0m[38;2;147;132;95mP[0m[38;2;175;157;112mA[0m[38;2;201;180;129mC[0m[38;2;225;202;144mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;205;144mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;221;195;135mC[0m[38;2;180;158;110mA[0m[38;2;134;118;82mP[0m[38;2;86;76;53mA[0m[38;2;38;34;24mR[0m[38;2;3;3;2m [0m
|
||||
[0m[38;2;9;8;6m [0m[38;2;38;34;25mR[0m[38;2;66;59;43mA[0m[38;2;94;84;60mP[0m[38;2;123;109;79mP[0m[38;2;151;135;97mP[0m[38;2;180;161;114mA[0m[38;2;209;190;115mC[0m[38;2;234;216;110m#[0m[38;2;238;221;100m#[0m[38;2;238;222;99m#[0m[38;2;237;219;106m#[0m[38;2;234;214;123m#[0m[38;2;230;207;143mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;206;146mC[0m[38;2;230;205;144mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;230;202;139mC[0m[38;2;211;186;129mC[0m[38;2;165;145;101mA[0m[38;2;117;103;72mP[0m[38;2;69;61;43mA[0m[38;2;22;19;14mR [0m
|
||||
|
@ -502,14 +511,16 @@ pub fn print_version(mut args: &mut Arguments) -> Result<(), Error> {
|
|||
[0m[38;2;25;22;16mR[0m[38;2;127;113;79mP[0m[38;2;216;192;132mC[0m[38;2;226;201;137mC[0m[38;2;224;199;129mC[0m[38;2;223;197;123mC[0m[38;2;223;197;123mC[0m[38;2;223;197;123mC[0m[38;2;211;186;117mC[0m[38;2;157;139;88mP[0m[38;2;103;91;58mP[0m[38;2;48;42;28mR[0m[38;2;5;4;3m [0m
|
||||
[0m[38;2;28;25;18mR[0m[38;2;137;123;85mP[0m[38;2;215;190;125mC[0m[38;2;174;154;98mA[0m[38;2;118;104;66mP[0m[38;2;61;54;35mA[0m[38;2;9;8;5m [0m\n\x1b[?7h");
|
||||
} else {
|
||||
println!("{name} v{version}-{suffix}-{release} ({timestamp})
|
||||
println!(
|
||||
"{name} v{version}-{suffix}-{release} ({timestamp})
|
||||
Copyright (C) 2023-2024 Xavier R.M.
|
||||
|
||||
Website: https://pacwrap.sapphirus.org/
|
||||
Github: https://github.com/sapphirusberyl/pacwrap
|
||||
|
||||
This program may be freely redistributed under the
|
||||
terms of the GNU General Public License v3 only.\n");
|
||||
terms of the GNU General Public License v3 only.\n"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,14 +17,19 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use alpm::{Alpm, PackageReason};
|
||||
use alpm::PackageReason;
|
||||
|
||||
use pacwrap_core::{config,
|
||||
constants::{RESET, BOLD_GREEN},
|
||||
utils::arguments::{Operand, InvalidArgument},
|
||||
utils::{arguments::Arguments, check_root},
|
||||
error::*,
|
||||
err};
|
||||
use pacwrap_core::{
|
||||
config,
|
||||
constants::{BOLD_GREEN, RESET},
|
||||
err,
|
||||
error::*,
|
||||
sync::instantiate_alpm,
|
||||
utils::{
|
||||
arguments::{Arguments, InvalidArgument, Operand},
|
||||
check_root,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn query(arguments: &mut Arguments) -> Result<()> {
|
||||
let mut target = "";
|
||||
|
@ -47,19 +52,18 @@ pub fn query(arguments: &mut Arguments) -> Result<()> {
|
|||
}
|
||||
|
||||
let handle = config::provide_handle(target)?;
|
||||
let root = handle.vars().root().as_ref();
|
||||
let handle = Alpm::new2(root, &format!("{}/var/lib/pacman/", root)).unwrap();
|
||||
let handle = instantiate_alpm(&handle);
|
||||
|
||||
for pkg in handle.localdb().pkgs() {
|
||||
if explicit && pkg.reason() != PackageReason::Explicit {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
match quiet {
|
||||
true => println!("{} ", pkg.name()),
|
||||
false => println!("{} {}{}{} ", pkg.name(), *BOLD_GREEN, pkg.version(), *RESET),
|
||||
}
|
||||
false => println!("{} {}{}{} ", pkg.name(), *BOLD_GREEN, pkg.version(), *RESET),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -19,16 +19,22 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pacwrap_core::{err,
|
||||
use pacwrap_core::{
|
||||
config::{cache, init::init},
|
||||
err,
|
||||
error::*,
|
||||
log::Logger,
|
||||
sync::transaction::TransactionType,
|
||||
utils::{arguments::Operand, check_root},
|
||||
utils::arguments::{Arguments, InvalidArgument},
|
||||
sync::transaction::{TransactionFlags, TransactionAggregator},
|
||||
config::{cache, init::init}};
|
||||
sync::transaction::{TransactionAggregator, TransactionFlags, TransactionType},
|
||||
utils::{
|
||||
arguments::{Arguments, InvalidArgument, Operand as Op},
|
||||
check_root,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn remove(mut args: &mut Arguments) -> Result<()> {
|
||||
check_root()?;
|
||||
init()?;
|
||||
|
||||
let mut logger = Logger::new("pacwrap-sync").init().unwrap();
|
||||
let action = {
|
||||
let mut recursive = 0;
|
||||
|
@ -36,82 +42,64 @@ pub fn remove(mut args: &mut Arguments) -> Result<()> {
|
|||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::Short('s') | Operand::Long("recursive") => recursive += 1,
|
||||
Operand::Short('c') | Operand::Long("cascade") => cascade = true,
|
||||
Op::Short('s') | Op::Long("recursive") => recursive += 1,
|
||||
Op::Short('c') | Op::Long("cascade") => cascade = true,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
TransactionType::Remove(recursive > 0 , cascade, recursive > 1)
|
||||
TransactionType::Remove(recursive > 0, cascade, recursive > 1)
|
||||
};
|
||||
|
||||
check_root()?;
|
||||
init()?;
|
||||
engage_aggregator(action, &mut args, &mut logger)
|
||||
}
|
||||
|
||||
fn engage_aggregator<'a>(
|
||||
action_type: TransactionType,
|
||||
args: &'a mut Arguments,
|
||||
log: &'a mut Logger) -> Result<()> {
|
||||
fn engage_aggregator<'a>(action_type: TransactionType, args: &'a mut Arguments, log: &'a mut Logger) -> Result<()> {
|
||||
let cache = cache::populate()?;
|
||||
let mut action_flags = TransactionFlags::NONE;
|
||||
let mut targets = Vec::new();
|
||||
let mut queue: HashMap<&'a str,Vec<&'a str>> = HashMap::new();
|
||||
let mut queue: HashMap<&'a str, Vec<&'a str>> = HashMap::new();
|
||||
let mut current_target = None;
|
||||
|
||||
if let Operand::None = args.next().unwrap_or_default() {
|
||||
if let Op::Nothing = args.next().unwrap_or_default() {
|
||||
err!(InvalidArgument::OperationUnspecified)?
|
||||
}
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::Long("remove")
|
||||
| Operand::Long("cascade")
|
||||
| Operand::Long("recursive")
|
||||
| Operand::Short('R')
|
||||
| Operand::Short('c')
|
||||
| Operand::Short('s')
|
||||
| Operand::Short('t') => continue,
|
||||
Operand::Long("noconfirm")
|
||||
=> action_flags = action_flags | TransactionFlags::NO_CONFIRM,
|
||||
Operand::Short('p')
|
||||
| Operand::Long("preview")
|
||||
=> action_flags = action_flags | TransactionFlags::PREVIEW,
|
||||
Operand::Long("dbonly")
|
||||
=> action_flags = action_flags | TransactionFlags::DATABASE_ONLY,
|
||||
Operand::Long("force-foreign")
|
||||
=> action_flags = action_flags | TransactionFlags::FORCE_DATABASE,
|
||||
Operand::Short('f')
|
||||
| Operand::Long("filesystem")
|
||||
=> action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC,
|
||||
Operand::ShortPos('t', target)
|
||||
| Operand::LongPos("target", target)
|
||||
| Operand::ShortPos(_, target) => {
|
||||
Op::Long("remove")
|
||||
| Op::Long("cascade")
|
||||
| Op::Long("recursive")
|
||||
| Op::Short('R')
|
||||
| Op::Short('c')
|
||||
| Op::Short('s')
|
||||
| Op::Short('t') => continue,
|
||||
Op::Long("dbonly") => action_flags = action_flags | TransactionFlags::DATABASE_ONLY,
|
||||
Op::Long("noconfirm") => action_flags = action_flags | TransactionFlags::NO_CONFIRM,
|
||||
Op::Long("force-foreign") => action_flags = action_flags | TransactionFlags::FORCE_DATABASE,
|
||||
Op::Short('p') | Op::Long("preview") => action_flags = action_flags | TransactionFlags::PREVIEW,
|
||||
Op::Short('f') | Op::Long("filesystem") => action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC,
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) | Op::ShortPos(_, target) => {
|
||||
cache.get_instance(target)?;
|
||||
current_target = Some(target);
|
||||
targets.push(target);
|
||||
},
|
||||
Operand::LongPos(_, package)
|
||||
| Operand::Value(package) => if let Some(target) = current_target {
|
||||
match queue.get_mut(target) {
|
||||
Some(vec) => vec.push(package),
|
||||
None => { queue.insert(target, vec!(package)); },
|
||||
}
|
||||
},
|
||||
}
|
||||
Op::LongPos(_, package) | Op::Value(package) =>
|
||||
if let Some(target) = current_target {
|
||||
match queue.get_mut(target) {
|
||||
Some(vec) => vec.push(package),
|
||||
None => {
|
||||
queue.insert(target, vec![package]);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => args.invalid_operand()?,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let None = current_target {
|
||||
err!(InvalidArgument::TargetUnspecified)?
|
||||
}
|
||||
|
||||
Ok(TransactionAggregator::new(&cache,
|
||||
queue,
|
||||
log,
|
||||
action_flags,
|
||||
action_type,
|
||||
current_target)
|
||||
.aggregate()?)
|
||||
Ok(TransactionAggregator::new(&cache, queue, log, action_flags, action_type, current_target).aggregate()?)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* pacwrap
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2023-2024 Xavier R.M. <sapphirus@azorium.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*
|
||||
|
@ -17,30 +17,31 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{collections::HashMap,
|
||||
fs::File,
|
||||
io::Write};
|
||||
use std::{collections::HashMap, fs::File, io::Write};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use pacwrap_core::{err,
|
||||
ErrorKind,
|
||||
use pacwrap_core::{
|
||||
config::{cache, init::init, InstanceCache, InstanceHandle, InstanceType},
|
||||
constants::{ARROW_GREEN, BAR_GREEN, BOLD, RESET},
|
||||
err,
|
||||
error::*,
|
||||
log::Logger,
|
||||
sync::transaction::TransactionType,
|
||||
utils::check_root,
|
||||
utils::arguments::{Arguments,
|
||||
InvalidArgument,
|
||||
Operand},
|
||||
config::{InstanceType,
|
||||
InstanceHandle,
|
||||
init::init,
|
||||
cache},
|
||||
config::InstanceCache,
|
||||
sync::{instantiate_trust, transaction::{TransactionFlags, TransactionAggregator}},
|
||||
constants::{BAR_GREEN, BOLD, RESET, ARROW_GREEN}};
|
||||
sync::{
|
||||
instantiate_trust,
|
||||
transaction::{TransactionAggregator, TransactionFlags, TransactionType},
|
||||
},
|
||||
utils::{
|
||||
arguments::{Arguments, InvalidArgument::*, Operand as Op},
|
||||
check_root,
|
||||
},
|
||||
ErrorKind,
|
||||
};
|
||||
|
||||
pub fn synchronize(args: &mut Arguments) -> Result<()> {
|
||||
let mut logger = Logger::new("pacwrap-sync").init().unwrap();
|
||||
check_root()?;
|
||||
init()?;
|
||||
|
||||
let mut logger = Logger::new("pacwrap-sync").init().unwrap();
|
||||
let mut cache = cache::populate()?;
|
||||
let action = {
|
||||
let mut u = 0;
|
||||
|
@ -48,8 +49,8 @@ pub fn synchronize(args: &mut Arguments) -> Result<()> {
|
|||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::Short('y') | Operand::Long("refresh") => y += 1,
|
||||
Operand::Short('u') | Operand::Long("upgrade") => u += 1,
|
||||
Op::Short('y') | Op::Long("refresh") => y += 1,
|
||||
Op::Short('u') | Op::Long("upgrade") => u += 1,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
@ -57,21 +58,18 @@ pub fn synchronize(args: &mut Arguments) -> Result<()> {
|
|||
TransactionType::Upgrade(u > 0, y > 0, y > 1)
|
||||
};
|
||||
|
||||
check_root()?;
|
||||
init()?;
|
||||
|
||||
if create(args) {
|
||||
if let TransactionType::Upgrade(upgrade, refresh, _) = action {
|
||||
if ! refresh {
|
||||
err!(InvalidArgument::UnsuppliedOperand("--refresh", "Required for container creation."))?
|
||||
} else if ! upgrade {
|
||||
err!(InvalidArgument::UnsuppliedOperand("--upgrade", "Required for container creation."))?
|
||||
if create(args) {
|
||||
if let TransactionType::Upgrade(upgrade, refresh, _) = action {
|
||||
if !refresh {
|
||||
err!(UnsuppliedOperand("--refresh", "Required for container creation."))?
|
||||
} else if !upgrade {
|
||||
err!(UnsuppliedOperand("--upgrade", "Required for container creation."))?
|
||||
}
|
||||
}
|
||||
|
||||
instantiate_trust()?;
|
||||
instantiate_trust()?;
|
||||
instantiate(&mut logger, &mut cache, acquire_depends(args)?)?;
|
||||
}
|
||||
}
|
||||
|
||||
engage_aggregator(&cache, action, args, &mut logger)
|
||||
}
|
||||
|
@ -81,72 +79,80 @@ fn acquire_depends<'a>(args: &mut Arguments<'a>) -> Result<IndexMap<&'a str, (In
|
|||
let mut current_target = "";
|
||||
let mut instype = None;
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::ShortPos('d', dep)
|
||||
| Operand::LongPos("dep", dep) => match deps.get_mut(current_target) {
|
||||
Some(d) => {
|
||||
if let Some(instype) = instype {
|
||||
if let InstanceType::BASE = instype {
|
||||
err!(ErrorKind::Message("Dependencies cannot be assigned to base containers."))?
|
||||
}
|
||||
Op::Short('b') | Op::Long("base") => instype = Some(InstanceType::Base),
|
||||
Op::Short('s') | Op::Long("slice") => instype = Some(InstanceType::Slice),
|
||||
Op::Short('a') | Op::Long("agg") => instype = Some(InstanceType::Aggregate),
|
||||
Op::ShortPos('d', dep) | Op::LongPos("dep", dep) => match instype {
|
||||
Some(instance) => {
|
||||
if let InstanceType::Base = instance {
|
||||
err!(ErrorKind::Message("Dependencies cannot be assigned to base containers."))?
|
||||
}
|
||||
|
||||
d.1.push(dep);
|
||||
},
|
||||
None => err!(InvalidArgument::TargetUnspecified)?
|
||||
|
||||
match dep.contains(",") {
|
||||
true =>
|
||||
for dep in dep.split(",") {
|
||||
match deps.get_mut(current_target) {
|
||||
Some(d) => d.1.push(dep),
|
||||
None => err!(TargetUnspecified)?,
|
||||
}
|
||||
},
|
||||
false => match deps.get_mut(current_target) {
|
||||
Some(d) => d.1.push(dep),
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
}
|
||||
}
|
||||
None => err!(TargetUnspecified)?,
|
||||
},
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => match instype {
|
||||
Some(instype) => {
|
||||
current_target = target;
|
||||
deps.insert(current_target, (instype, vec![]));
|
||||
}
|
||||
None => err!(ErrorKind::Message("Container type not specified."))?,
|
||||
},
|
||||
Operand::Short('b')
|
||||
| Operand::Long("base") => instype = Some(InstanceType::BASE),
|
||||
Operand::Short('s')
|
||||
| Operand::Long("slice") => instype = Some(InstanceType::DEP),
|
||||
Operand::Short('r')
|
||||
| Operand::Long("root") => instype = Some(InstanceType::ROOT),
|
||||
Operand::ShortPos('t', target)
|
||||
| Operand::LongPos("target", target) => match instype {
|
||||
Some(instype) => {
|
||||
current_target = target;
|
||||
deps.insert(current_target, (instype, vec!()));
|
||||
},
|
||||
None => err!(ErrorKind::Message("Container type not specified."))?,
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
for dep in deps.iter() {
|
||||
if let InstanceType::BASE = dep.1.0 {
|
||||
if let InstanceType::Base = dep.1 .0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if dep.1.1.is_empty() {
|
||||
err!(ErrorKind::Message("Dependencies not specified."))?
|
||||
}
|
||||
|
||||
if dep.1 .1.is_empty() {
|
||||
err!(ErrorKind::Message("Dependencies not specified."))?
|
||||
}
|
||||
}
|
||||
|
||||
if current_target.len() == 0 {
|
||||
err!(InvalidArgument::TargetUnspecified)?
|
||||
err!(TargetUnspecified)?
|
||||
}
|
||||
|
||||
Ok(deps)
|
||||
}
|
||||
|
||||
|
||||
fn create(args: &mut Arguments) -> bool {
|
||||
for arg in args {
|
||||
if let Operand::Short('c') | Operand::Long("create") = arg {
|
||||
for arg in args {
|
||||
if let Op::Short('c') | Op::Long("create") = arg {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn instantiate<'a>(logger: &mut Logger, cache: &mut InstanceCache<'a>, targets: IndexMap<&'a str, (InstanceType, Vec<&'a str>)>) -> Result<()> {
|
||||
println!("{} {}Instantiating container...{}{}", *BAR_GREEN, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET);
|
||||
fn instantiate<'a>(
|
||||
logger: &mut Logger,
|
||||
cache: &mut InstanceCache<'a>,
|
||||
targets: IndexMap<&'a str, (InstanceType, Vec<&'a str>)>,
|
||||
) -> Result<()> {
|
||||
println!("{} {}Instantiating container{}...{}", *BAR_GREEN, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET);
|
||||
|
||||
for target in targets {
|
||||
cache.add(target.0, target.1.0, target.1.1)?;
|
||||
cache.add(target.0, target.1 .0, target.1 .1)?;
|
||||
instantiate_container(logger, cache.get_instance(target.0)?)?;
|
||||
}
|
||||
|
||||
|
@ -158,10 +164,10 @@ fn instantiate_container<'a>(logger: &mut Logger, handle: &'a InstanceHandle<'a>
|
|||
let instype = handle.metadata().container_type();
|
||||
|
||||
if let Err(err) = std::fs::create_dir(handle.vars().root()) {
|
||||
err!(ErrorKind::IOError(handle.vars().root().into(), err.kind()))?
|
||||
err!(ErrorKind::IOError(handle.vars().root().into(), err.kind()))?
|
||||
}
|
||||
|
||||
if let InstanceType::ROOT | InstanceType::BASE = instype {
|
||||
if let InstanceType::Aggregate | InstanceType::Base = instype {
|
||||
if let Err(err) = std::fs::create_dir(handle.vars().home()) {
|
||||
if err.kind() != std::io::ErrorKind::AlreadyExists {
|
||||
err!(ErrorKind::IOError(handle.vars().root().into(), err.kind()))?
|
||||
|
@ -169,13 +175,14 @@ fn instantiate_container<'a>(logger: &mut Logger, handle: &'a InstanceHandle<'a>
|
|||
}
|
||||
|
||||
let bashrc = format!("{}/.bashrc", handle.vars().home());
|
||||
|
||||
|
||||
match File::create(&bashrc) {
|
||||
Ok(mut f) => if let Err(error) = write!(f, "PS1=\"$USER> \"") {
|
||||
err!(ErrorKind::IOError(bashrc, error.kind()))?
|
||||
},
|
||||
Err(error) => err!(ErrorKind::IOError(bashrc.clone(), error.kind()))?
|
||||
};
|
||||
Ok(mut f) =>
|
||||
if let Err(error) = write!(f, "PS1=\"$USER> \"") {
|
||||
err!(ErrorKind::IOError(bashrc, error.kind()))?
|
||||
},
|
||||
Err(error) => err!(ErrorKind::IOError(bashrc.clone(), error.kind()))?,
|
||||
};
|
||||
}
|
||||
|
||||
handle.save()?;
|
||||
|
@ -186,94 +193,85 @@ fn instantiate_container<'a>(logger: &mut Logger, handle: &'a InstanceHandle<'a>
|
|||
|
||||
fn engage_aggregator<'a>(
|
||||
cache: &InstanceCache<'a>,
|
||||
action_type: TransactionType,
|
||||
args: &'a mut Arguments,
|
||||
log: &'a mut Logger) -> Result<()> {
|
||||
action_type: TransactionType,
|
||||
args: &'a mut Arguments,
|
||||
log: &'a mut Logger,
|
||||
) -> Result<()> {
|
||||
let mut action_flags = TransactionFlags::NONE;
|
||||
let mut targets = Vec::new();
|
||||
let mut queue: HashMap<&'a str ,Vec<&'a str>> = HashMap::new();
|
||||
let mut queue: HashMap<&'a str, Vec<&'a str>> = HashMap::new();
|
||||
let mut current_target = "";
|
||||
let mut base = false;
|
||||
|
||||
if let Operand::None = args.next().unwrap_or_default() {
|
||||
err!(InvalidArgument::OperationUnspecified)?
|
||||
if let Op::Nothing = args.next().unwrap_or_default() {
|
||||
err!(OperationUnspecified)?
|
||||
}
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg {
|
||||
Operand::Short('d')
|
||||
| Operand::Long("dep") | Operand::LongPos("dep", _)
|
||||
| Operand::Short('s') | Operand::Long("slice")
|
||||
| Operand::Short('r') | Operand::Long("root")
|
||||
| Operand::Short('t') | Operand::Long("target")
|
||||
| Operand::Short('y') | Operand::Long("refresh")
|
||||
| Operand::Short('u') | Operand::Long("upgrade") => continue,
|
||||
Operand::Short('o')
|
||||
| Operand::Long("target-only")
|
||||
=> action_flags = action_flags | TransactionFlags::TARGET_ONLY,
|
||||
Operand::Short('f')
|
||||
| Operand::Long("filesystem")
|
||||
=> action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC,
|
||||
Operand::Short('p')
|
||||
| Operand::Long("preview")
|
||||
=> action_flags = action_flags | TransactionFlags::PREVIEW,
|
||||
Operand::Short('c')
|
||||
| Operand::Long("create")
|
||||
=> action_flags = action_flags | TransactionFlags::CREATE
|
||||
| TransactionFlags::FORCE_DATABASE,
|
||||
Operand::Short('b') |
|
||||
Operand::Long("base") => base = true,
|
||||
Operand::Long("dbonly")
|
||||
=> action_flags = action_flags | TransactionFlags::DATABASE_ONLY,
|
||||
Operand::Long("force-foreign")
|
||||
=> action_flags = action_flags | TransactionFlags::FORCE_DATABASE,
|
||||
Operand::Long("noconfirm")
|
||||
=> action_flags = action_flags | TransactionFlags::NO_CONFIRM,
|
||||
Operand::ShortPos('t', target)
|
||||
| Operand::LongPos("target", target) => {
|
||||
Op::Short('a')
|
||||
| Op::Short('s')
|
||||
| Op::Short('d')
|
||||
| Op::Short('t')
|
||||
| Op::Short('y')
|
||||
| Op::Short('u')
|
||||
| Op::Long("aggregate")
|
||||
| Op::Long("slice")
|
||||
| Op::Long("dep")
|
||||
| Op::Long("target")
|
||||
| Op::Long("refresh")
|
||||
| Op::Long("upgrade")
|
||||
| Op::LongPos("dep", _) => continue,
|
||||
Op::Short('b') | Op::Long("base") => base = true,
|
||||
Op::Short('o') | Op::Long("target-only") => action_flags = action_flags | TransactionFlags::TARGET_ONLY,
|
||||
Op::Short('f') | Op::Long("filesystem") => action_flags = action_flags | TransactionFlags::FILESYSTEM_SYNC,
|
||||
Op::Short('p') | Op::Long("preview") => action_flags = action_flags | TransactionFlags::PREVIEW,
|
||||
Op::Long("dbonly") => action_flags = action_flags | TransactionFlags::DATABASE_ONLY,
|
||||
Op::Long("force-foreign") => action_flags = action_flags | TransactionFlags::FORCE_DATABASE,
|
||||
Op::Long("noconfirm") => action_flags = action_flags | TransactionFlags::NO_CONFIRM,
|
||||
Op::Short('c') | Op::Long("create") =>
|
||||
action_flags = action_flags | TransactionFlags::CREATE | TransactionFlags::FORCE_DATABASE,
|
||||
Op::ShortPos('t', target) | Op::LongPos("target", target) => {
|
||||
cache.get_instance(target)?;
|
||||
current_target = target;
|
||||
targets.push(target);
|
||||
|
||||
if base {
|
||||
queue.insert(current_target.into(), vec!("base", "pacwrap-base-dist"));
|
||||
base = false;
|
||||
if base {
|
||||
queue.insert(current_target.into(), vec!["base", "pacwrap-base-dist"]);
|
||||
base = false;
|
||||
}
|
||||
},
|
||||
Operand::LongPos(_, package)
|
||||
| Operand::Value(package) => if current_target != "" {
|
||||
match queue.get_mut(current_target.into()) {
|
||||
Some(vec) => vec.push(package.into()),
|
||||
None => { queue.insert(current_target.into(), vec!(package)); },
|
||||
}
|
||||
},
|
||||
_ => args.invalid_operand()?
|
||||
}
|
||||
Op::LongPos(_, package) | Op::Value(package) =>
|
||||
if current_target != "" {
|
||||
match queue.get_mut(current_target.into()) {
|
||||
Some(vec) => vec.push(package.into()),
|
||||
None => {
|
||||
queue.insert(current_target.into(), vec![package]);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => args.invalid_operand()?,
|
||||
}
|
||||
}
|
||||
|
||||
let current_target = match action_flags.intersects(TransactionFlags::TARGET_ONLY) {
|
||||
true => {
|
||||
if current_target == "" && ! action_flags.intersects(TransactionFlags::FILESYSTEM_SYNC) {
|
||||
err!(InvalidArgument::TargetUnspecified)?
|
||||
if current_target == "" && !action_flags.intersects(TransactionFlags::FILESYSTEM_SYNC) {
|
||||
err!(TargetUnspecified)?
|
||||
}
|
||||
|
||||
Some(current_target)
|
||||
},
|
||||
}
|
||||
false => {
|
||||
if let TransactionType::Upgrade(upgrade, refresh, _) = action_type {
|
||||
if ! upgrade && ! refresh {
|
||||
err!(InvalidArgument::OperationUnspecified)?
|
||||
if !upgrade && !refresh {
|
||||
err!(OperationUnspecified)?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(TransactionAggregator::new(cache,
|
||||
queue,
|
||||
log,
|
||||
action_flags,
|
||||
action_type,
|
||||
current_target).aggregate()?)
|
||||
Ok(TransactionAggregator::new(cache, queue, log, action_flags, action_type, current_target).aggregate()?)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue