diff --git a/pacwrap-agent/src/agent.rs b/pacwrap-agent/src/agent.rs index 463db18..ec1d106 100644 --- a/pacwrap-agent/src/agent.rs +++ b/pacwrap-agent/src/agent.rs @@ -33,9 +33,9 @@ use pacwrap_core::{err, TransactionMetadata, TransactionParameters, MAGIC_NUMBER}, - event::{download::{DownloadCallback, download_event}, - progress::{ProgressEvent, callback}, - query::questioncb}}, + event::{download::{self, DownloadEvent}, + progress::{self, ProgressEvent}, + query}}, utils::{print_warning, read_le_32}, config::Global}; use crate::error::AgentError; @@ -70,13 +70,17 @@ pub fn transact() -> Result<()> { let alpm = sync::instantiate_alpm_agent(&config, &alpm_remotes); let mut handle = TransactionHandle::new(&config, alpm, &mut metadata); - conduct_transaction(&mut handle, params) + conduct_transaction(&config, &mut handle, params) } -fn conduct_transaction(handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> { +fn conduct_transaction(config: &Global, handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> { let flags = handle.retrieve_flags(); let mode = agent.mode(); let action = agent.action(); + let config = config.config(); + let progress = config.progress(); + let bytes = agent.bytes(); + let files = agent.files(); if let Err(error) = handle.alpm_mut().trans_init(flags.1.unwrap()) { err!(SyncError::InitializationFailure(error.to_string().into()))? @@ -96,9 +100,17 @@ fn conduct_transaction(handle: &mut TransactionHandle, agent: TransactionParamet erroneous_preparation(error)? } - handle.alpm().set_progress_cb(ProgressEvent::new(&action), callback(&mode)); - handle.alpm().set_question_cb((), questioncb); - handle.alpm().set_dl_cb(DownloadCallback::new(agent.bytes(), agent.files()), download_event); + 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); + + 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)); if let Err(error) = handle.alpm_mut().trans_commit() { erroneous_transaction(error)? diff --git a/pacwrap-core/src/config.rs b/pacwrap-core/src/config.rs index dd83ab1..9b2cdae 100644 --- a/pacwrap-core/src/config.rs +++ b/pacwrap-core/src/config.rs @@ -45,7 +45,7 @@ pub mod cache; pub mod instance; pub mod init; pub mod register; -mod global; +pub mod global; #[derive(Debug, Clone)] pub enum ConfigError { @@ -64,8 +64,8 @@ impl Display for ConfigError { 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}.yml': {error}"), - Self::Save(ins, error) => write!(fmter, "Failed to save '{ins}.yml': {error}"), + 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) } diff --git a/pacwrap-core/src/config/cache.rs b/pacwrap-core/src/config/cache.rs index 02ca99b..ef87993 100644 --- a/pacwrap-core/src/config/cache.rs +++ b/pacwrap-core/src/config/cache.rs @@ -54,7 +54,13 @@ impl <'a>InstanceCache<'a> { err!(ConfigError::AlreadyExists(ins.into()))? } - let deps = deps.iter().map(|a| (*a).into()).collect(); + 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!()); @@ -79,7 +85,7 @@ impl <'a>InstanceCache<'a> { fn map(&mut self, ins: &'a str) -> Result<()> { if let Some(_) = self.instances.get(ins) { - err!(ConfigError::AlreadyExists(ins.into()))? + err!(ConfigError::AlreadyExists(ins.to_owned()))? } Ok(self.register(ins, match config::provide_handle(ins) { @@ -125,7 +131,13 @@ impl <'a>InstanceCache<'a> { } } - pub fn get_instance(&self, ins: &str) -> Option<&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())), + } + } + + pub fn get_instance_option(&self, ins: &str) -> Option<&InstanceHandle> { self.instances.get(ins) } } @@ -146,24 +158,19 @@ pub fn populate<'a>() -> Result> { fn roots<'a>() -> Result> { match read_dir(format!("{}/root", *DATA_DIR)) { - Ok(dir) => Ok(dir.filter(|f| match f { + Ok(dir) => Ok(dir.filter(|f| match f { Ok(f) => match f.metadata() { - Ok(meta) => meta.is_dir() | meta.is_symlink(), - Err(_) => false, + Ok(meta) => meta.is_dir() | meta.is_symlink(), Err(_) => false }, - Err(_) => false }) + Err(_) => false + }) .map(|s| match s { - Ok(f) => f.file_name() - .to_str() - .unwrap_or("") - .to_string() - .leak(), + Ok(f) => match f.file_name().to_str() { + Some(f) => f.to_owned().leak(), None => "", + }, Err(_) => "", }) - .filter_map(|e| match e.is_empty() { - true => None, - false => Some(e) - }) + .filter(|e| ! e.is_empty()) .collect()), Err(error) => err!(ErrorKind::IOError(format!("{}/root", *DATA_DIR), error.kind())), } diff --git a/pacwrap-core/src/config/global.rs b/pacwrap-core/src/config/global.rs index 16458db..b59c627 100644 --- a/pacwrap-core/src/config/global.rs +++ b/pacwrap-core/src/config/global.rs @@ -35,12 +35,27 @@ pub enum Verbosity { Verbose, } +#[derive(Serialize, Deserialize, Clone)] +pub enum ProgressKind { + Simple, + Condensed, + CondensedForeign, + CondensedLocal, + Verbose, +} + impl Default for Verbosity { fn default() -> Self { Self::Verbose } } +impl Default for ProgressKind { + fn default() -> Self { + Self::CondensedForeign + } +} + #[derive(Serialize, Deserialize, Clone)] pub struct Global { #[serde(default = "Configuration::new")] @@ -49,12 +64,22 @@ pub struct Global { alpm: AlpmConfiguration, } +#[derive(Serialize, Deserialize, Clone)] +pub struct Progress { + #[serde(default = "ProgressKind::default")] + transact: ProgressKind, + #[serde(default = "ProgressKind::default")] + download: ProgressKind, +} + #[derive(Serialize, Deserialize, Clone)] pub struct Configuration { #[serde(default = "Verbosity::default")] summary: Verbosity, #[serde(default = "Verbosity::default")] - progress: Verbosity, + logging: Verbosity, + #[serde(default = "Progress::new")] + progress: Progress, } #[derive(Serialize, Deserialize, Clone)] @@ -79,7 +104,29 @@ impl Configuration { fn new() -> Self { Self { summary: Verbosity::Basic, - progress: Verbosity::Verbose, + logging: Verbosity::Basic, + progress: Progress::new(), + } + } + + pub fn progress(&self) -> (&ProgressKind, &ProgressKind) { + (&self.progress.transact, &self.progress.download) + } + + pub fn logging(&self) -> &Verbosity { + &self.summary + } + + pub fn summary(&self) -> &Verbosity { + &self.summary + } +} + +impl Progress { + fn new() -> Self { + Self { + transact: ProgressKind::CondensedForeign, + download: ProgressKind::CondensedForeign, } } } diff --git a/pacwrap-core/src/config/init.rs b/pacwrap-core/src/config/init.rs index 5925458..fe9716b 100644 --- a/pacwrap-core/src/config/init.rs +++ b/pacwrap-core/src/config/init.rs @@ -23,7 +23,8 @@ use crate::{err, Error, Result, ErrorKind, - constants::{CACHE_DIR, CONFIG_DIR, DATA_DIR}}; + constants::{CACHE_DIR, CONFIG_DIR, DATA_DIR}, + config::global::CONFIG}; 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. @@ -44,8 +45,11 @@ static PACWRAP_CONF_DEFAULT: &'static str = r###"## See the pacwrap.yml(2) manpa ## Documentation is also available at https://git.sapphirus.org/pacwrap/pacwrap/docs/ config: - summary: Basic - progress: Verbose + logging: Basic + summmary: Basic + #progress: + #transact: CondensedForeign + #download: CondensedForeign alpm: #ignore_pkg: #- somepackage @@ -84,7 +88,7 @@ impl DirectoryLayout { fn data_layout() -> DirectoryLayout { DirectoryLayout { - dirs: vec!("/root", "/home", "/state", "/pacman/gnupg", "/pacman/sync"), + dirs: vec!("/root", "/home", "/state", "/pacman/sync"), root: *DATA_DIR, } } @@ -121,6 +125,8 @@ fn write_to_file(location: &str, contents: &str) -> Result<()> { } pub fn init() -> Result<()> { + let _ = *CONFIG; + config_layout().instantiate()?; data_layout().instantiate()?; cache_layout().instantiate()?; diff --git a/pacwrap-core/src/config/instance.rs b/pacwrap-core/src/config/instance.rs index cc5bb76..9afc416 100644 --- a/pacwrap-core/src/config/instance.rs +++ b/pacwrap-core/src/config/instance.rs @@ -18,7 +18,6 @@ */ use std::{borrow::Cow, - time::{SystemTime, UNIX_EPOCH}, fmt::{Display, Debug, Formatter}, vec::Vec}; @@ -29,7 +28,8 @@ use crate::{Result, filesystem::{Filesystem, root::ROOT, home::HOME}, dbus::Dbus, vars::InsVars, - save}}; + save}, + constants::UNIX_TIMESTAMP}; #[derive(Serialize, Deserialize, Clone)] pub struct Instance<'a> { @@ -228,14 +228,14 @@ impl <'a>InstanceMetadata<'a> { container_type: ctype, dependencies: deps.iter().map(|a| (*a).into()).collect(), explicit_packages: pkgs.iter().map(|a| (*a).into()).collect(), - meta_version: time_as_seconds(), + meta_version: *UNIX_TIMESTAMP, } } pub fn set(&mut self, deps: Vec<&'a str>, pkgs: Vec<&'a str>) { self.dependencies = deps.iter().map(|a| (*a).into()).collect(); self.explicit_packages = pkgs.iter().map(|a| (*a).into()).collect(); - self.meta_version = time_as_seconds(); + self.meta_version = *UNIX_TIMESTAMP; } pub fn container_type(&self) -> &InstanceType { @@ -249,15 +249,16 @@ impl <'a>InstanceMetadata<'a> { pub fn explicit_packages(&'a self) -> Vec<&'a str> { self.explicit_packages.iter().map(|a| a.as_ref()).collect() } -} -fn time_as_seconds() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() + pub fn timestamp(&self) -> u64 { + self.meta_version + } } fn default_true() -> bool { true } + +fn time_as_seconds() -> u64 { + *UNIX_TIMESTAMP +} diff --git a/pacwrap-core/src/constants.rs b/pacwrap-core/src/constants.rs index 11ddfcd..cefc8b7 100644 --- a/pacwrap-core/src/constants.rs +++ b/pacwrap-core/src/constants.rs @@ -22,11 +22,12 @@ use std::{env::var, process::id}; use lazy_static::lazy_static; use nix::unistd::{geteuid, getegid}; -use crate::{error, Error, ErrorKind, utils::{is_color_terminal, is_truecolor_terminal}}; +use crate::{error, Error, ErrorKind, utils::{is_color_terminal, is_truecolor_terminal, unix_time_as_seconds}}; 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"; const PACWRAP_CONFIG_DIR: &str = "/.config/pacwrap"; const PACWRAP_DATA_DIR: &str = "/.local/share/pacwrap"; @@ -66,6 +67,7 @@ lazy_static! { 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); pub static ref LOG_LOCATION: &'static str = format_str!("{}/pacwrap.log", *DATA_DIR); + pub static ref UNIX_TIMESTAMP: u64 = unix_time_as_seconds(); pub static ref IS_COLOR_TERMINLAL: bool = is_color_terminal(); pub static ref IS_TRUECOLOR_TERMINLAL: bool = is_truecolor_terminal(); pub static ref BOLD: &'static str = bold(); diff --git a/pacwrap-core/src/exec.rs b/pacwrap-core/src/exec.rs index a4b1e42..e4596f5 100644 --- a/pacwrap-core/src/exec.rs +++ b/pacwrap-core/src/exec.rs @@ -17,12 +17,10 @@ * along with this program. If not, see . */ -use std::{process::{Child, Command}, fmt::{Formatter, Display}, os::fd::AsRawFd}; +use std::{process::{Child, Command, Stdio}, fmt::{Formatter, Display}}; use command_fds::{CommandFdExt, FdMapping}; use lazy_static::lazy_static; -use os_pipe::{PipeWriter, PipeReader}; -use serde::Serialize; use crate::{err, impl_error, @@ -31,10 +29,10 @@ use crate::{err, ErrorKind, Error, Result, - constants::{LOG_LOCATION, BWRAP_EXECUTABLE, BOLD, RESET, GID, UID, TERM, LANG, COLORTERM}, - config::{InstanceHandle, CONFIG}, - sync::{DEFAULT_ALPM_CONF, SyncError, transaction::{TransactionParameters, TransactionMetadata}}, - exec::seccomp::{provide_bpf_program, FilterType::*}}; + constants::{LOG_LOCATION, BWRAP_EXECUTABLE, BOLD, RESET, GID, UID, TERM, LANG, COLORTERM, PACMAN_KEY_SCRIPT}, + config::InstanceHandle, + sync::transaction::{TransactionParameters, TransactionMetadata}, + exec::{utils::{wait_on_process, agent_params}, seccomp::{provide_bpf_program, FilterType::*}}}; pub mod args; pub mod utils; @@ -173,17 +171,15 @@ pub fn transaction_agent(ins: &InstanceHandle, params: &TransactionParameters, m } } -fn agent_params(reader: &PipeReader, writer: &PipeWriter, params: &TransactionParameters, metadata: &TransactionMetadata) -> Result { - serialize(params, writer)?; - serialize(&*CONFIG, writer)?; - serialize(&*DEFAULT_ALPM_CONF, writer)?; - serialize(metadata, writer)?; - Ok(reader.as_raw_fd()) -} - -fn serialize Serialize>(input: &T, file: &PipeWriter) -> Result<()> { - match bincode::serialize_into::<&PipeWriter, T>(file, input) { - Ok(()) => Ok(()), - Err(error) => err!(SyncError::TransactionFailure(format!("Agent data serialization failed: {}", error))), +pub fn pacman_key(path: &str, cmd: Vec<&str>) -> Result<()> { + match Command::new(PACMAN_KEY_SCRIPT) + .stderr(Stdio::null()) + .env("EUID", "0") + .arg("--gpgdir") + .arg(path) + .args(cmd) + .spawn() { + Ok(proc) => wait_on_process(PACMAN_KEY_SCRIPT, proc), + Err(error) => err!(ErrorKind::ProcessInitFailure(PACMAN_KEY_SCRIPT, error.kind()))?, } } diff --git a/pacwrap-core/src/exec/utils.rs b/pacwrap-core/src/exec/utils.rs index bdba658..b47c1b3 100644 --- a/pacwrap-core/src/exec/utils.rs +++ b/pacwrap-core/src/exec/utils.rs @@ -17,12 +17,20 @@ * along with this program. If not, see . */ -use std::{process::Child, io::Read}; +use std::{process::Child, io::Read, os::fd::AsRawFd}; use os_pipe::{PipeReader, PipeWriter}; +use serde::Serialize; use serde_yaml::Value; -use crate::{constants::BWRAP_EXECUTABLE, config::InstanceHandle, ErrorKind, error::*, err}; +use crate::{err, + error::*, + ErrorKind, + constants::BWRAP_EXECUTABLE, + config::{InstanceHandle, CONFIG}, + sync::{SyncError, transaction::{TransactionParameters, + TransactionMetadata}, + DEFAULT_ALPM_CONF}}; pub fn execute_fakeroot_container(ins: &InstanceHandle, arguments: Vec<&str>) -> Result<()> { match super::fakeroot_container(ins, arguments)?.wait() { @@ -46,13 +54,31 @@ pub fn bwrap_json(mut reader: PipeReader, writer: PipeWriter) -> Result { } } -pub fn handle_process(name: &str, result: std::result::Result) -> Result<()> { +pub fn agent_params(reader: &PipeReader, writer: &PipeWriter, params: &TransactionParameters, metadata: &TransactionMetadata) -> Result { + serialize(params, writer)?; + serialize(&*CONFIG, writer)?; + serialize(&*DEFAULT_ALPM_CONF, writer)?; + serialize(metadata, writer)?; + Ok(reader.as_raw_fd()) +} + +fn serialize Serialize>(input: &T, file: &PipeWriter) -> Result<()> { + match bincode::serialize_into::<&PipeWriter, T>(file, input) { + Ok(()) => Ok(()), + Err(error) => err!(SyncError::TransactionFailure(format!("Agent data serialization failed: {}", error))), + } +} + +pub fn handle_process(name: &'static str, result: std::result::Result) -> Result<()> { match result { - Ok(child) => Ok(wait_on_process(child)), + Ok(child) => wait_on_process(name, child), Err(error) => err!(ErrorKind::IOError(name.into(), error.kind())), } } -fn wait_on_process(mut child: Child) { - child.wait().ok(); +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())) + } } diff --git a/pacwrap-core/src/sync.rs b/pacwrap-core/src/sync.rs index 5e4454f..4c15d69 100644 --- a/pacwrap-core/src/sync.rs +++ b/pacwrap-core/src/sync.rs @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -use std::fmt::{Display, Formatter}; +use std::{fmt::{Display, Formatter}, path::Path}; use alpm::{Alpm, SigLevel, Usage}; use lazy_static::lazy_static; @@ -26,12 +26,14 @@ use serde::{Serialize, Deserialize}; use crate::{err, impl_error, error::*, + ErrorKind, utils::print_warning, + exec::pacman_key, constants::{BAR_GREEN, RESET, BOLD, CACHE_DIR, DATA_DIR, CONFIG_DIR, ARROW_RED}, - sync::event::download::{DownloadCallback, download_event}, + sync::event::download::{self, DownloadEvent}, config::{InsVars, InstanceHandle, - cache::InstanceCache, CONFIG, Global}}; + cache::InstanceCache, CONFIG, Global, global::ProgressKind}}; pub mod event; pub mod utils; @@ -64,6 +66,7 @@ pub enum SyncError { InitializationFailure(String), InternalError(String), NoCompatibleRemotes, + UnableToLocateKeyrings, } impl_error!(SyncError); @@ -85,6 +88,7 @@ impl Display for SyncError { 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::NothingToDo(_) => write!(fmter, "Nothing to do."), _ => Ok(()), }?; @@ -124,7 +128,7 @@ impl AlpmConfigData { remotes.push(("pacwrap".into(), (SigLevel::PACKAGE_MARGINAL_OK | SigLevel::DATABASE_MARGINAL_OK).bits(), - vec![format!("file://{}", env!("PACWRAP_DIST_REPO")), format!("file:///mnt/dist-repo/")])); + vec![format!("file://{}", env!("PACWRAP_DIST_REPO")), format!("file:///mnt/share/dist-repo/")])); Self { repos: remotes, @@ -149,7 +153,6 @@ pub fn instantiate_alpm_agent(config: &Global, remotes: &AlpmConfigData) -> 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 { @@ -166,6 +169,29 @@ fn alpm_handle(insvars: &InsVars, db_path: String, remotes: &AlpmConfigData) -> handle } +//TODO: Port pacman-key to Rust + +pub fn instantiate_trust() -> Result<()> { + let path = &format!("{}/pacman/gnupg/", *DATA_DIR); + + if Path::new(path).exists() { + return Ok(()); + } + + println!("{} {}Initializing package trust database...{}", *BAR_GREEN, *BOLD, *RESET); + + if ! Path::new("/usr/share/pacman/keyrings").exists() { + err!(SyncError::UnableToLocateKeyrings)? + } + + if let Err(error) = std::fs::create_dir_all(path) { + err!(ErrorKind::IOError(path.into(), error.kind()))? + } + + pacman_key(path, vec!["--init"])?; + pacman_key(path, vec!["--populate"]) +} + fn register_remote(mut handle: Alpm, config: &AlpmConfigData) -> Alpm { for repo in &config.repos { let core = handle.register_syncdb_mut(repo.0.clone(), @@ -187,8 +213,8 @@ fn synchronize_database(cache: &InstanceCache, force: bool) -> Result<()> { let db_path = format!("{}/pacman/", *DATA_DIR); let mut handle = alpm_handle(&ins.vars(), db_path, &*DEFAULT_ALPM_CONF); - println!("{} {}Synchronising package databases...{}", *BAR_GREEN, *BOLD, *RESET); - handle.set_dl_cb(DownloadCallback::new(0, 0), download_event); + 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()))? @@ -226,7 +252,7 @@ fn signature(sigs: &Vec, default: SigLevel) -> SigLevel { let mut sig = SigLevel::empty(); for level in sigs { - sig = sig | if level == "PackageRequired" || level == "PackageTrustedOnly" { + sig = sig | if level == "Required" || level == "PackageRequired" { SigLevel::PACKAGE } else if level == "DatabaseRequired" || level == "DatabaseTrustedOnly" { SigLevel::DATABASE diff --git a/pacwrap-core/src/sync/event.rs b/pacwrap-core/src/sync/event.rs index 5b62f03..94e99fe 100644 --- a/pacwrap-core/src/sync/event.rs +++ b/pacwrap-core/src/sync/event.rs @@ -22,14 +22,25 @@ pub mod query; pub mod progress; fn whitespace(total: usize, current: usize) -> String { + let total = log10(total); + let current = log10(current); let mut whitespace = String::new(); let difference = total-current; - if difference > 0 { - for _ in 0..difference { - whitespace.push_str(" "); - } - } + for _ in 0..difference { + whitespace.push_str(" "); + } whitespace } + +fn log10(mut value: usize) -> usize { + let mut length = 0; + + while value > 0 { + value /= 10; + length += 1; + } + + length +} diff --git a/pacwrap-core/src/sync/event/download.rs b/pacwrap-core/src/sync/event/download.rs index 1f3beb4..fe4bee0 100644 --- a/pacwrap-core/src/sync/event/download.rs +++ b/pacwrap-core/src/sync/event/download.rs @@ -19,16 +19,18 @@ use std::collections::HashMap; -use alpm::{AnyDownloadEvent, DownloadEvent}; +use alpm::{AnyDownloadEvent, DownloadEvent as Event}; use dialoguer::console::Term; -use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle, ProgressDrawTarget}; use lazy_static::lazy_static; +use simplebyteunit::simplebyteunit::*; + +use crate::{constants::{ARROW_CYAN, BOLD, RESET}, config::global::ProgressKind, sync::transaction::TransactionMode}; use super::whitespace; lazy_static!{ - static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}") - .unwrap(); + 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("#-") @@ -36,104 +38,185 @@ lazy_static!{ } #[derive(Clone)] -pub struct DownloadCallback { +pub struct DownloadEvent { + total: usize, + position: usize, + total_bar: Option, + condensed: bool, progress: MultiProgress, - prbar: HashMap, - style: ProgressStyle, - total_files: usize, - total_files_len: usize, - files_done: usize + bars: HashMap<&'static str, ProgressBar>, + style: Option, } -impl DownloadCallback { - pub fn new(total_bytes: u64, files: usize) -> Self { - let mut bars = HashMap::new(); - let files_str_len = files.to_string().len(); - let size = Term::size(&Term::stdout()); - let width = ((size.1 / 2) - 14).to_string(); - let multiprogress = MultiProgress::new(); - let pb_style_tmpl = " {spinner:.green} {msg:<".to_owned()+width.as_str() - + "} {bytes:>11} {bytes_per_sec:>12} {elapsed_precise:>5} [{wide_bar}] {percent:<3}%"; - let pb_style = ProgressStyle::with_template(&(pb_style_tmpl)) - .unwrap() - .progress_chars("#-") - .tick_strings(&[" ", "✓"]); - - if total_bytes > 0 { - let pb_total = multiprogress.add( - ProgressBar::new(total_bytes) - .with_style(pb_style.clone()) - .with_message(format!("Total ({}0/{})", whitespace(files_str_len, 1), files))); - - bars.insert("total".into(), pb_total); - } - +impl DownloadEvent { + pub fn new() -> Self { Self { - style: pb_style, - total_files: files, - total_files_len: files_str_len, - files_done: 0, - progress: multiprogress, - prbar: bars, + total: 0, + position: 0, + total_bar: None, + condensed: false, + progress: MultiProgress::new(), + bars: HashMap::new(), + style: None, } } + + pub fn style(mut self, kind: &ProgressKind) -> Self { + 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(&[" ", "✓"]) + }), + }; + self + } + + pub fn total(mut self, bytes: u64, files: usize) -> Self { + 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))); + + bar.set_position(0); + bar + }), + _ => None, + }; + + self + } + + pub fn configure(mut self, mode: &TransactionMode, progress: &ProgressKind) -> Self { + self.condensed = match progress { + ProgressKind::Condensed => true, + ProgressKind::CondensedForeign => match mode { + TransactionMode::Foreign => true, + TransactionMode::Local => false, + }, + ProgressKind::CondensedLocal => match mode { + TransactionMode::Foreign => false, + TransactionMode::Local => true, + }, + _ => false, + }; + self + } + + fn increment(&mut self, progress: u64) { + let bar = match self.total_bar.as_mut() { + 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(); + } + } + + 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)), + false => self.progress.insert_before(&total, ProgressBar::new(0)), + }, + None => self.progress.add(ProgressBar::new(0)) + }; + + pb.set_style(INIT.clone()); + pb.set_message(message(file)); + + /* + * alpm-rs expects our callback signature to provide a struct bound by a 'static lifetime, + * therefore allocate this literal and then leak it for use as a key. + */ + self.bars.insert(file.to_owned().leak(), pb); + } } -pub fn download_event(file: &str, download: AnyDownloadEvent, this: &mut DownloadCallback) { - if file.ends_with(".sig") { +pub fn simple(file: &str, download: AnyDownloadEvent, this: &mut DownloadEvent) { + if file.ends_with(".sig") { + return; + } + + if let Event::Completed(progress) = download.event() { + this.position += 1; + + let size = progress.total.abs().to_byteunit(SI); + let total = this.total; + let pos = this.position; + let whitespace = whitespace(total, pos); + let message = message(file); + + 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; } match download.event() { - DownloadEvent::Progress(progress) => { - if let Some(pb) = this.prbar.get_mut(&file.to_string()) { - if pb.length().unwrap() == 0 { + 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.clone()); + pb.set_style(this.style.as_ref().unwrap().clone()); } - pb.set_position(progress.downloaded.unsigned_abs()); - - if let Some(total) = this.prbar.get("total") { - total.inc(progress.downloaded.unsigned_abs()); - } + pb.set_position(progress.downloaded.unsigned_abs()); } }, - DownloadEvent::Completed(_) => { - if let Some(pb) = this.prbar.remove(&file.to_string()) { + 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 let Some(total) = this.prbar.get("total") { - this.files_done += 1; - - total.set_message(format!("Total ({}{}/{})", - whitespace(this.total_files_len, - this.files_done.to_string().len()), - this.files_done, - this.total_files)); - - if this.files_done == this.total_files { - total.finish(); - } + if this.condensed { + pb.set_draw_target(ProgressDrawTarget::hidden()); } } }, - DownloadEvent::Init(_) => { - let pb = if let Some(total) = this.prbar.get("total") { - this.progress.insert_before(&total, ProgressBar::new(0)) - } else { - this.progress.add(ProgressBar::new(0)) - }; + Event::Init(progress) => { + if progress.optional { + return; + } - pb.set_style(INIT.clone()); - pb.set_message(message(file)); - this.prbar.insert(file.into(), pb); + this.insert(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) { + match progress { + ProgressKind::Simple => simple, _ => event } } diff --git a/pacwrap-core/src/sync/event/progress.rs b/pacwrap-core/src/sync/event/progress.rs index da935fe..f7a7d3f 100644 --- a/pacwrap-core/src/sync/event/progress.rs +++ b/pacwrap-core/src/sync/event/progress.rs @@ -17,114 +17,168 @@ * along with this program. If not, see . */ -use alpm::Progress; +use alpm::Progress as Event; use dialoguer::console::Term; use indicatif::{ProgressBar, ProgressStyle}; -use crate::{constants::{BOLD, RESET}, sync::transaction::{TransactionType, TransactionMode}}; -use super::whitespace; +use crate::{constants::{BOLD, RESET, ARROW_CYAN}, + sync::{event::whitespace, transaction::{TransactionType, TransactionMode}}, + config::global::ProgressKind}; #[derive(Clone)] pub struct ProgressEvent { - progress: ProgressBar, + current: Option, + progress: Option, offset: usize, - style: ProgressStyle, - current: String, + style: Option, } impl ProgressEvent { - pub fn new(state: &TransactionType) -> Self { - let size = Term::size(&Term::stdout()); - let width = (size.1 / 2).to_string(); - + pub fn new() -> Self { Self { - offset: state.pr_offset(), - style: ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned()+width.as_str()+"} [{wide_bar}] {percent:<3}%")) - .unwrap().progress_chars("#-").tick_strings(&[" ", "✓"]), - progress: ProgressBar::new(0), - current: "".into(), + current: None, + progress: None, + style: None, + offset: 0, } } + + pub fn configure(mut self, state: &TransactionType) -> Self { + self.offset = state.pr_offset(); + self + } + + pub fn style(mut self, kind: &ProgressKind) -> Self { + 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(&[" ", "✓"]) + }), + }; + self + } + + fn bar(&mut self, ident: Option, name: &str, howmany: usize, current: usize, size: usize) { + let pos = current + self.offset; + let total = howmany + self.offset; + let 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_style(self.style.as_ref().unwrap().clone()); + + self.progress = Some(progress); + self.current = ident + } } -pub fn event(progress: Progress, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) { - let ident = ident(progress,pkgname); +pub fn event(event: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) { + let ident = ident(event,pkgname); - if ident != this.current { - let pos = current + this.offset; - let total = howmany + this.offset; - let progress_name: String = name(progress,pkgname); - let whitespace = whitespace(total.to_string().len(), pos.to_string().len()); - - this.progress = ProgressBar::new(100); - this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET)); - this.progress.set_style(this.style.clone()); - this.current = ident; - } - - this.progress.set_position(percent as u64); + 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), + }; + + let progress = match this.progress.as_mut() { + Some(progress) => progress, None => return, + }; + + progress.set_position(percent as u64); if percent == 100 { - this.progress.finish(); + progress.finish(); } } -pub fn condensed(progress: Progress, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) { - if let Progress::AddStart | Progress::RemoveStart | Progress::UpgradeStart = progress { +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 whitespace = whitespace(total.to_string().len(), pos.to_string().len()); + let whitespace = whitespace(total, pos); - if this.current != "" { - this.progress = ProgressBar::new(howmany as u64); - this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET)); - this.progress.set_style(this.style.clone()); - this.progress.set_position(current as u64); - this.current = "".into(); - } else { - this.progress.set_position(current as u64); - this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET)); - } + eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET); if current == howmany { - this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) Foreign synchronization complete", *BOLD, *RESET, *BOLD, *RESET)); - this.progress.finish(); + 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 { + let pos = current + this.offset; + let total = howmany + this.offset; + let progress_name: String = name(kind,pkgname); + let whitespace = whitespace(total, pos); + + if let Some(_) = this.current { + this.bar(None, &progress_name, howmany, current, howmany); + } + + let progress = match this.progress.as_mut() { + Some(progress) => progress, None => return, + }; + + progress.set_position(current as u64); + + if current != howmany { + 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(); } } else { - event(progress, pkgname, percent, howmany, current, this) + event(kind, pkgname, percent, howmany, current, this) } } -fn name(progress: Progress, pkgname: &str) -> String { - match progress { - Progress::KeyringStart => "Loading keyring".into(), - Progress::IntegrityStart => "Checking integrity".into(), - Progress::LoadStart => "Loading packages".into(), - Progress::ConflictsStart => "Checking conflicts".into(), - Progress::DiskspaceStart => "Checking available diskspace".into(), - Progress::UpgradeStart => format!("Upgrading {}", pkgname), - Progress::AddStart => format!("Installing {}", pkgname), - Progress::RemoveStart => format!("Removing {}", pkgname), - Progress::DowngradeStart => format!("Downgrading {}", pkgname), - Progress::ReinstallStart => format!("Reinstalling {}", pkgname) +fn name(progress: Event, pkgname: &str) -> String { + 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::IntegrityStart => format!("Checking integrity"), + Event::LoadStart => format!("Loading packages"), + Event::ConflictsStart => format!("Checking conflicts"), + Event::DiskspaceStart => format!("Checking available diskspace"), } } -fn ident(progress: Progress, pkgname: &str) -> String { +fn ident(progress: Event, pkgname: &str) -> String { match progress { - Progress::KeyringStart => "keyring", - Progress::IntegrityStart => "integrity", - Progress::LoadStart => "loadstart", - Progress::ConflictsStart => "conflicts", + Event::KeyringStart => "keyring", + Event::IntegrityStart => "integrity", + Event::LoadStart => "loadstart", + Event::ConflictsStart => "conflicts", _ => pkgname - }.into() + }.to_owned() } -pub fn callback(state: &TransactionMode) -> for<'a, 'b> fn(Progress, &'a str, i32, usize, usize, &'b mut ProgressEvent) { - match state { - TransactionMode::Local => event, - TransactionMode::Foreign => condensed, +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 { + TransactionMode::Local => event, + TransactionMode::Foreign => condensed, + }, + ProgressKind::CondensedLocal => match state { + TransactionMode::Local => condensed, + TransactionMode::Foreign => event, + }, + ProgressKind::Condensed => condensed, + ProgressKind::Verbose => event, } } diff --git a/pacwrap-core/src/sync/event/query.rs b/pacwrap-core/src/sync/event/query.rs index 6139df4..e2048f4 100644 --- a/pacwrap-core/src/sync/event/query.rs +++ b/pacwrap-core/src/sync/event/query.rs @@ -23,7 +23,7 @@ use alpm::{AnyQuestion, Question::*}; use crate::utils::prompt::prompt; -pub fn questioncb(question: AnyQuestion, _: &mut ()) { +pub fn callback(question: AnyQuestion, _: &mut ()) { match question.question() { Conflict(mut x) => { let pkg_a = x.conflict().package1(); @@ -64,9 +64,6 @@ pub fn questioncb(question: AnyQuestion, _: &mut ()) { x.set_import(true); } }, - //TODO: Implement these questions. - RemovePkgs(_) => (), - SelectProvider(_) => (), - InstallIgnorepkg(_) => (), + _ => (), } } diff --git a/pacwrap-core/src/sync/filesystem.rs b/pacwrap-core/src/sync/filesystem.rs index 5d9dac4..667b0db 100644 --- a/pacwrap-core/src/sync/filesystem.rs +++ b/pacwrap-core/src/sync/filesystem.rs @@ -147,11 +147,8 @@ impl <'a>FileSystemStateSync<'a> { continue; } - let inshandle = match self.cache.get_instance(ins) { - Some(ins) => ins, - None => err!(ErrorKind::InstanceNotFound(ins.to_string()))? - }; - + let inshandle = self.cache.get_instance(ins)?; + write_chan = self.link(&inshandle.metadata().dependencies(), write_chan)?; if let ROOT = inshandle.metadata().container_type() { diff --git a/pacwrap-core/src/sync/transaction.rs b/pacwrap-core/src/sync/transaction.rs index 2513c25..fff18f3 100644 --- a/pacwrap-core/src/sync/transaction.rs +++ b/pacwrap-core/src/sync/transaction.rs @@ -34,7 +34,8 @@ use crate::{err, uptodate::UpToDate}, resolver_local::LocalDependencyResolver, resolver::DependencyResolver, - utils::AlpmUtils}, utils::print_warning}; + utils::AlpmUtils}, + utils::print_warning}; pub use self::aggregator::TransactionAggregator; @@ -95,16 +96,16 @@ bitflags! { pub struct TransactionHandle<'a> { meta: &'a mut TransactionMetadata<'a>, + config: &'a Global, alpm: Option, fail: bool, - config: &'a Global, + deps: Option>, } #[derive(Serialize, Deserialize, Clone)] pub struct TransactionMetadata<'a> { foreign_pkgs: HashSet, resident_pkgs: HashSet, - deps: Option>, queue: Vec>, mode: TransactionMode, flags: (u8, u32) @@ -203,8 +204,7 @@ impl <'a>TransactionMetadata<'a> { fn new(queue: Vec<&'a str>) -> TransactionMetadata { Self { foreign_pkgs: HashSet::new(), - resident_pkgs: HashSet::new(), - deps: None, + resident_pkgs: HashSet::new(), mode: TransactionMode::Local, queue: queue.iter().map(|q| (*q).into()).collect::>(), flags: (0, 0), @@ -217,6 +217,7 @@ impl <'a>TransactionHandle<'a> { Self { meta: metadata, alpm: Some(alpm_handle), + deps: None, fail: true, config: global, } @@ -230,7 +231,7 @@ impl <'a>TransactionHandle<'a> { }; for pkg in alpm.localdb().pkgs() { - if let Some(_) = ignored.get(pkg.name().into()) { + if let Some(_) = ignored.get(pkg.name()) { continue; } @@ -242,22 +243,32 @@ impl <'a>TransactionHandle<'a> { SyncReqResult::NotRequired } - fn enumerate_foreign_pkgs(&mut self, dep_handle: &Alpm) { - self.meta.foreign_pkgs.extend(dep_handle.localdb() + fn enumerate_package_lists(&mut self, dep_handle: &Alpm, queue: bool) { + let foreign_pkgs = dep_handle.localdb() .pkgs() .iter() - .map(|p| p.name().into()) - .filter(|p| ! self.meta.foreign_pkgs.contains(p)) - .collect::>()); - self.meta.resident_pkgs.extend(self.alpm() + .filter(|p| ! self.meta.foreign_pkgs.contains(p.name())) + .map(|p| p.name().to_owned()) + .collect::>(); + let resident_pkgs = self.alpm() .localdb() .pkgs() .iter() - .map(|a| a.name().into()) - .filter(|p| ! self.meta.foreign_pkgs.contains(p) - && ! self.meta.resident_pkgs.contains(p)) - .collect::>()); - } + .filter(|p| ! self.meta.foreign_pkgs.contains(p.name()) + && ! self.meta.resident_pkgs.contains(p.name())) + .map(|a| a.name().to_owned()) + .collect::>(); + + if queue { + self.meta.queue.extend(foreign_pkgs.iter() + .map(|p| p.to_owned().into()) + .collect::>()); + self.fail = false; + } + + 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; @@ -329,10 +340,9 @@ impl <'a>TransactionHandle<'a> { .filter(|a| ignored.contains(*a)) .collect::>(); - if ! flags.contains(TransactionFlags::FORCE_DATABASE) { - if ! upstream.is_empty() { - err!(SyncError::TargetUpstream(upstream[0].to_string()))? - } + if ! flags.contains(TransactionFlags::FORCE_DATABASE) + && ! upstream.is_empty() { + err!(SyncError::TargetUpstream(upstream[0].into()))? } } @@ -345,7 +355,7 @@ impl <'a>TransactionHandle<'a> { .collect::>(); if ! not_installed.is_empty() { - err!(SyncError::TargetNotInstalled(not_installed[0].to_string()))? + err!(SyncError::TargetNotInstalled(not_installed[0].into()))? } for pkg in LocalDependencyResolver::new(alpm, &ignored, trans_type).enumerate(&queue)? { @@ -359,22 +369,20 @@ impl <'a>TransactionHandle<'a> { .collect::>(); if ! not_available.is_empty() { - err!(SyncError::TargetNotAvailable(not_available[0].to_string()))? + err!(SyncError::TargetNotAvailable(not_available[0].into()))? } let packages = DependencyResolver::new(alpm, &ignored).enumerate(&queue)?; for pkg in packages.1 { - if let None = self.meta.foreign_pkgs.get(pkg.name()) { - if let TransactionMode::Foreign = self.meta.mode { - continue; - } + if let (None, TransactionMode::Foreign) = (self.meta.foreign_pkgs.get(pkg.name()), self.meta.mode) { + continue; } alpm.trans_add_pkg(pkg).unwrap(); } - self.meta.deps = packages.0; + self.deps = packages.0; } } @@ -419,7 +427,7 @@ impl <'a>TransactionHandle<'a> { } pub fn mark_depends(&mut self) { - if let Some(deps) = self.meta.deps.as_ref() { + if let Some(deps) = self.deps.as_ref() { for mut pkg in deps.iter().filter_map(|a| self.alpm().get_local_package(a)) { pkg.set_reason(PackageReason::Depend).unwrap(); } diff --git a/pacwrap-core/src/sync/transaction/aggregator.rs b/pacwrap-core/src/sync/transaction/aggregator.rs index 4a70fc3..cbdbc9c 100644 --- a/pacwrap-core/src/sync/transaction/aggregator.rs +++ b/pacwrap-core/src/sync/transaction/aggregator.rs @@ -22,7 +22,7 @@ use std::collections::HashMap; use crate::{err, ErrorKind, error::*, - constants::ARROW_GREEN, + constants::{ARROW_GREEN, UNIX_TIMESTAMP}, config::InstanceType, exec::utils::execute_fakeroot_container, log::Logger, @@ -72,6 +72,7 @@ impl <'a>TransactionAggregator<'a> { } pub fn aggregate(mut self) -> Result<()> { + let _timestamp = *UNIX_TIMESTAMP; let upgrade = match self.action { TransactionType::Upgrade(upgrade, refresh, force) => { if refresh { @@ -83,8 +84,7 @@ impl <'a>TransactionAggregator<'a> { _ => false, }; let target = match self.target { - Some(s) => self.cache.get_instance(s), - None => None + Some(s) => self.cache.get_instance_option(s), None => None }; if let Some(inshandle) = target { @@ -128,11 +128,11 @@ impl <'a>TransactionAggregator<'a> { continue; } - if let Some(inshandle) = self.cache.get_instance(ins) { - self.queried.push(ins); - self.transaction(&inshandle.metadata().dependencies())?; - self.transact(inshandle)?; - } + let inshandle = self.cache.get_instance(ins).unwrap(); + + self.queried.push(ins); + self.transaction(&inshandle.metadata().dependencies())?; + self.transact(inshandle)?; } Ok(()) diff --git a/pacwrap-core/src/sync/transaction/prepare.rs b/pacwrap-core/src/sync/transaction/prepare.rs index cda892f..5098761 100644 --- a/pacwrap-core/src/sync/transaction/prepare.rs +++ b/pacwrap-core/src/sync/transaction/prepare.rs @@ -29,7 +29,7 @@ use crate::{err, TransactionHandle, TransactionAggregator, TransactionFlags, - SyncReqResult}}}; + SyncReqResult}}, constants::UNIX_TIMESTAMP}; pub struct Prepare { state: TransactionState, @@ -46,21 +46,26 @@ impl Transaction for Prepare { match self.state { TransactionState::Prepare => { let deps: Vec<&str> = inshandle.metadata().dependencies(); - + let instype = inshandle.metadata().container_type(); + let action = ag.action(); + if deps.len() > 0 { for dep in deps.iter().rev() { - match ag.cache().get_instance(dep) { - Some(dep_handle) => { - let dep_alpm = sync::instantiate_alpm(dep_handle); - handle.enumerate_foreign_pkgs(&dep_alpm); - dep_alpm.release().unwrap(); + match ag.cache().get_instance_option(dep) { + 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, ..) = ag.action() { + if let TransactionType::Upgrade(upgrade, ..) = action { if ! upgrade && handle.metadata().queue.len() == 0 { err!(SyncError::NothingToDo(true))? } @@ -76,9 +81,9 @@ impl Transaction for Prepare { } } - if let TransactionType::Remove(..) = ag.action() { + if let TransactionType::Remove(..) = action { Ok(TransactionState::Stage) - } else if let InstanceType::BASE = inshandle.metadata().container_type() { + } else if let InstanceType::BASE = instype { Ok(TransactionState::Stage) } else { Ok(TransactionState::PrepareForeign) diff --git a/pacwrap-core/src/utils.rs b/pacwrap-core/src/utils.rs index 1b3bd40..41bf863 100644 --- a/pacwrap-core/src/utils.rs +++ b/pacwrap-core/src/utils.rs @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -use std::{path::Path, env::var, os::unix::net::UnixStream, fmt::Display}; +use std::{path::Path, env::var, os::unix::net::UnixStream, fmt::Display, time::{SystemTime, UNIX_EPOCH}}; use nix::unistd::isatty; @@ -63,6 +63,13 @@ pub fn is_truecolor_terminal() -> bool { } +pub fn unix_time_as_seconds() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() +} + pub fn read_le_32(vec: &Vec, pos: usize) -> u32 { ((vec[pos+0] as u32) << 0) + ((vec[pos+1] as u32) << 8) + ((vec[pos+2] as u32) << 16) + ((vec[pos+3] as u32) << 24) } diff --git a/pacwrap/src/compat.rs b/pacwrap/src/compat.rs index 984fbf7..9191eb3 100644 --- a/pacwrap/src/compat.rs +++ b/pacwrap/src/compat.rs @@ -58,7 +58,7 @@ pub fn compat(args: &mut Arguments) -> Result<()> { } } -pub fn execute_bash(executable: &str, args: &mut Arguments) -> Result<()> { +pub fn execute_bash(executable: &'static str, args: &mut Arguments) -> Result<()> { handle_process(&executable, Command::new(&executable) .args(args.values()) .spawn()) diff --git a/pacwrap/src/remove.rs b/pacwrap/src/remove.rs index c870b91..530b8d8 100644 --- a/pacwrap/src/remove.rs +++ b/pacwrap/src/remove.rs @@ -19,14 +19,14 @@ use std::collections::HashMap; -use pacwrap_core::{log::Logger, +use pacwrap_core::{err, + error::*, + log::Logger, sync::transaction::TransactionType, utils::{arguments::Operand, check_root}, utils::arguments::{Arguments, InvalidArgument}, sync::transaction::{TransactionFlags, TransactionAggregator}, - config::{cache, init::init}, - error::*, - err, ErrorKind}; + config::{cache, init::init}}; pub fn remove(mut args: &mut Arguments) -> Result<()> { let mut logger = Logger::new("pacwrap-sync").init().unwrap(); @@ -88,10 +88,7 @@ fn engage_aggregator<'a>( Operand::ShortPos('t', target) | Operand::LongPos("target", target) | Operand::ShortPos(_, target) => { - if let None = cache.get_instance(target) { - err!(ErrorKind::InstanceNotFound(target.into()))? - } - + cache.get_instance(target)?; current_target = Some(target); targets.push(target); }, diff --git a/pacwrap/src/sync.rs b/pacwrap/src/sync.rs index 1bf85ff..ebe6875 100644 --- a/pacwrap/src/sync.rs +++ b/pacwrap/src/sync.rs @@ -36,7 +36,7 @@ use pacwrap_core::{err, init::init, cache}, config::InstanceCache, - sync::transaction::{TransactionFlags, TransactionAggregator}, + sync::{instantiate_trust, transaction::{TransactionFlags, TransactionAggregator}}, constants::{BAR_GREEN, BOLD, RESET, ARROW_GREEN}}; pub fn synchronize(args: &mut Arguments) -> Result<()> { @@ -69,8 +69,9 @@ pub fn synchronize(args: &mut Arguments) -> Result<()> { } } - instantiate(&mut logger, &mut cache, acquire_depends(args)?)? - } + instantiate_trust()?; + instantiate(&mut logger, &mut cache, acquire_depends(args)?)?; + } engage_aggregator(&cache, action, args, &mut logger) } @@ -142,21 +143,11 @@ fn create(args: &mut Arguments) -> bool { } 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); + println!("{} {}Instantiating container...{}{}", *BAR_GREEN, *BOLD, if targets.len() > 1 { "s" } else { "" }, *RESET); for target in targets { - for dep in target.1.1.iter() { - if let None = cache.get_instance(dep) { - err!(ErrorKind::DependencyNotFound((*dep).into(), target.0.into()))? - } - } - cache.add(target.0, target.1.0, target.1.1)?; - - match cache.get_instance(target.0) { - Some(ins) => instantiate_container(logger, ins)?, - None => err!(ErrorKind::InstanceNotFound(target.0.into()))? - } + instantiate_container(logger, cache.get_instance(target.0)?)?; } Ok(()) @@ -240,10 +231,7 @@ fn engage_aggregator<'a>( => action_flags = action_flags | TransactionFlags::NO_CONFIRM, Operand::ShortPos('t', target) | Operand::LongPos("target", target) => { - if let None = cache.get_instance(target) { - err!(ErrorKind::InstanceNotFound(target.into()))? - } - + cache.get_instance(target)?; current_target = target; targets.push(target);