From b84f90d17d89326a099c9c98c12d35d941712e55 Mon Sep 17 00:00:00 2001 From: Xavier Date: Sat, 23 Dec 2023 23:00:31 -0500 Subject: [PATCH] Ciborium replaced with bincode, and pacwrap-agent attained UX parity - Refactored libalpm events into their own module - Agent parameters are acquired via a temporary file attached to the container (Still TODO: Provide a shared communication channel) - filesystem state files now contain magic values with file header verification - Miscellaneous tidy up work --- pacwrap-agent/src/agent.rs | 109 ++++++++------ pacwrap-agent/src/main.rs | 11 +- pacwrap-core/src/constants.rs | 1 + pacwrap-core/src/exec/utils.rs | 8 +- pacwrap-core/src/sync.rs | 8 +- pacwrap-core/src/sync/event.rs | 16 ++ .../sync/{dl_event.rs => event/download.rs} | 5 +- .../{progress_event.rs => event/progress.rs} | 13 +- .../sync/{query_event.rs => event/query.rs} | 0 pacwrap-core/src/sync/filesystem.rs | 139 +++++++----------- pacwrap-core/src/sync/transaction.rs | 65 +++++++- pacwrap-core/src/sync/transaction/commit.rs | 105 +++++++------ pacwrap-core/src/sync/utils.rs | 12 -- pacwrap-core/src/utils.rs | 5 +- pacwrap-core/src/utils/arguments.rs | 2 - 15 files changed, 283 insertions(+), 216 deletions(-) create mode 100644 pacwrap-core/src/sync/event.rs rename pacwrap-core/src/sync/{dl_event.rs => event/download.rs} (96%) rename pacwrap-core/src/sync/{progress_event.rs => event/progress.rs} (94%) rename pacwrap-core/src/sync/{query_event.rs => event/query.rs} (100%) diff --git a/pacwrap-agent/src/agent.rs b/pacwrap-agent/src/agent.rs index 1b69d15..49743de 100644 --- a/pacwrap-agent/src/agent.rs +++ b/pacwrap-agent/src/agent.rs @@ -1,54 +1,71 @@ -use std::{fs, io::Stdin, process::exit}; +use std::{fs::{self, File}, process::exit, os::unix::prelude::FileExt, env}; use serde::Deserialize; -use pacwrap_core::{constants::{BOLD, RESET}, - utils::{print_error, print_warning}, - sync::{self, AlpmConfigData, - query_event, - progress_event::{self, ProgressEvent}, +use pacwrap_core::{sync::{self, AlpmConfigData, utils::{erroneous_transaction, erroneous_preparation}, - transaction::{TransactionMetadata, - TransactionHandle, - TransactionType, + transaction::{TransactionHandle, + TransactionType, + TransactionMetadata, + TransactionParameters, Error, - Result}}}; + Result, MAGIC_NUMBER}, + event::{download::{DownloadCallback, download_event}, progress::{ProgressEvent, callback}, query::questioncb}}, + utils::{print_error, print_warning, read_le_32}, constants::{RESET, BOLD}}; pub fn transact() { - let mut stdin = std::io::stdin(); - let alpm_remotes: AlpmConfigData = deserialize_stdin(&mut stdin); - let mode: TransactionType = deserialize_stdin(&mut stdin); - let alpm = sync::instantiate_alpm_agent(&alpm_remotes); - let mut meta: TransactionMetadata = deserialize_stdin(&mut stdin); - let mut handle = TransactionHandle::new(alpm, &mut meta); + let mut header_buffer = vec![0; 7]; + let mut file = match File::open("/tmp/agent_params") { + Ok(file) => file, + Err(_) => { + if let Ok(var) = env::var("SHELL") { + if ! var.is_empty() { + print_error("Direct execution of this binary is unsupported."); + } + } - if let Err(error) = conduct_transaction(&mut handle, mode) { + exit(2); + }, + }; + + if let Err(error) = file.read_exact_at(&mut header_buffer, 0) { + print_error(format!("'{}/tmp/agent_params{}': {error}", *BOLD, *RESET)); + exit(3); + } + + let magic = read_le_32(&header_buffer, 0); + let major: u8 = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(); + let minor: u8 = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(); + let patch: u8 = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(); + + if magic != MAGIC_NUMBER { + print_error(format!("Magic number {magic} != {MAGIC_NUMBER}")); + exit(4); + } + + if major != header_buffer[4] || minor != header_buffer[5] || patch != header_buffer[6] { + print_error(format!("{major}.{minor}.{patch} != {}.{}.{}", header_buffer[4], header_buffer[5], header_buffer[6])); + exit(5); + } + + let params: TransactionParameters = deserialize(&mut file); + let alpm_remotes: AlpmConfigData = deserialize(&mut file); + let mut metadata: TransactionMetadata = deserialize(&mut file); + let alpm = sync::instantiate_alpm_agent(&alpm_remotes); + let mut handle = TransactionHandle::new(alpm, &mut metadata); + + if let Err(error) = conduct_transaction(&mut handle, params) { print_error(error); handle.alpm_mut().trans_release().ok(); exit(1); - } + } } -fn deserialize_stdin Deserialize<'de>>(stdin: &mut Stdin) -> T { - match ciborium::from_reader::(stdin) { - Ok(meta) => meta, - Err(err) => { - if let ciborium::de::Error::Semantic(_, error) = err { - match error.contains("integer `10`") { - false => print_error(format!("Deserialization failure occurred with input from {}STDIN{}: {error}", *BOLD, *RESET)), - true => print_error("Interactive user input is not supported by this program."), - } - } - - exit(1); - } - } -} - -fn conduct_transaction(handle: &mut TransactionHandle, action: TransactionType) -> Result<()> { +fn conduct_transaction(handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> { let flags = handle.retrieve_flags(); - let mode = handle.get_mode().clone(); + let mode = agent.mode(); + let action = agent.action(); if let Err(error) = handle.alpm_mut().trans_init(flags.1.unwrap()) { Err(Error::InitializationFailure(error.to_string().into()))? @@ -68,9 +85,9 @@ fn conduct_transaction(handle: &mut TransactionHandle, action: TransactionType) erroneous_preparation(error)? } - handle.alpm().set_progress_cb(ProgressEvent::new(&action), progress_event::callback(&mode)); - handle.alpm().set_question_cb((), query_event::questioncb); - //handle.alpm().set_dl_cb(DownloadCallback::new(), dl_event::download_event); + 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); if let Err(error) = handle.alpm_mut().trans_commit() { erroneous_transaction(error)? @@ -78,10 +95,20 @@ fn conduct_transaction(handle: &mut TransactionHandle, action: TransactionType) handle.alpm_mut().trans_release().unwrap(); handle.mark_depends(); - + if let Err(error) = fs::copy("/etc/ld.so.cache", "/mnt/etc/ld.so.cache") { print_warning(format!("Failed to propagate ld.so.cache: {}", error)); } - + Ok(()) } + +fn deserialize Deserialize<'de>>(stdin: &mut File) -> T { + match bincode::deserialize_from::<&mut File, T>(stdin) { + Ok(meta) => meta, + Err(error) => { + print_error(format!("Deserilization error: {}", error.as_ref())); + exit(3); + } + } +} diff --git a/pacwrap-agent/src/main.rs b/pacwrap-agent/src/main.rs index 1b7a6fb..8de849b 100644 --- a/pacwrap-agent/src/main.rs +++ b/pacwrap-agent/src/main.rs @@ -1,5 +1,4 @@ use pacwrap_core::utils::{print_error, Arguments, arguments::Operand}; -use serde::{Serialize, Deserialize}; mod agent; @@ -9,14 +8,6 @@ fn main() { match param { Operand::Value("transact") => agent::transact(), - _ => print_error(arguments.invalid_operand().to_string()) + _ => print_error("Direct execution of this binary is unsupported.") } } - - -#[derive(Serialize, Deserialize, Clone)] -struct Test { - string: String, - version_major: i16, - version_minor: i16, -} diff --git a/pacwrap-core/src/constants.rs b/pacwrap-core/src/constants.rs index f4e3cab..36803c4 100644 --- a/pacwrap-core/src/constants.rs +++ b/pacwrap-core/src/constants.rs @@ -18,6 +18,7 @@ lazy_static! { pub static ref CACHE_DIR: &'static str = env_default("PACWRAP_CACHE_DIR", PACWRAP_CACHE_DIR); pub static ref CONFIG_DIR: &'static str = env_default("PACWRAP_CONFIG_DIR", PACWRAP_CONFIG_DIR); pub static ref DATA_DIR: &'static str = env_default("PACWRAP_DATA_DIR", PACWRAP_DATA_DIR); + pub static ref PACWRAP_AGENT_FILE: &'static str = format!("/run/user/{}/pacwrap_agent_{}", geteuid(), &id()).leak(); pub static ref XDG_RUNTIME_DIR: String = format!("/run/user/{}", geteuid()); pub static ref DBUS_SOCKET: String = format!("/run/user/{}/pacwrap_dbus_{}", geteuid(), &id()); pub static ref LOG_LOCATION: &'static str = format!("{}/pacwrap.log", *DATA_DIR).leak(); diff --git a/pacwrap-core/src/exec/utils.rs b/pacwrap-core/src/exec/utils.rs index c40cfd1..7b35a00 100644 --- a/pacwrap-core/src/exec/utils.rs +++ b/pacwrap-core/src/exec/utils.rs @@ -1,6 +1,6 @@ -use std::{process::{Child, Command, Stdio}, io::Error, env::var}; +use std::{process::{Child, Command}, io::Error, env::var}; -use crate::{constants::{BWRAP_EXECUTABLE, self}, +use crate::{constants::{BWRAP_EXECUTABLE, self, PACWRAP_AGENT_FILE}, config::InstanceHandle, ErrorKind}; @@ -10,11 +10,11 @@ pub fn execute_agent(ins: &InstanceHandle) -> Result { Command::new(BWRAP_EXECUTABLE) .env_clear() - .stdin(Stdio::piped()) .arg("--bind").arg(&ins.vars().root()).arg("/mnt") .arg("--tmpfs").arg("/tmp") .arg("--tmpfs").arg("/etc") - .arg("--symlink").arg("/mnt/usr").arg("/usr") + .arg("--symlink").arg("/mnt/usr").arg("/usr") + .arg("--ro-bind").arg(*PACWRAP_AGENT_FILE).arg("/tmp/agent_params") .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") diff --git a/pacwrap-core/src/sync.rs b/pacwrap-core/src/sync.rs index a574793..9fc5a41 100644 --- a/pacwrap-core/src/sync.rs +++ b/pacwrap-core/src/sync.rs @@ -7,7 +7,7 @@ use serde::{Serialize, Deserialize}; use crate::{utils::{print_warning, print_error}, constants::{BAR_GREEN, RESET, BOLD, ARROW_RED, CACHE_DIR, DATA_DIR, CONFIG_DIR}, - sync::dl_event::DownloadCallback, + sync::event::download::{DownloadCallback, download_event}, config::{InsVars, InstanceHandle, cache::InstanceCache}}; @@ -18,9 +18,7 @@ lazy_static! { static ref DEFAULT_ALPM_CONF: AlpmConfigData = AlpmConfigData::new(); } -pub mod progress_event; -pub mod dl_event; -pub mod query_event; +pub mod event; pub mod utils; pub mod transaction; pub mod filesystem; @@ -104,7 +102,7 @@ fn synchronize_database(cache: &InstanceCache, force: bool) { 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), dl_event::download_event); + handle.set_dl_cb(DownloadCallback::new(0, 0), download_event); if let Err(err) = handle.syncdbs_mut().update(force) { print_error(format!("Unable to initialize transaction: {}.",err.to_string())); diff --git a/pacwrap-core/src/sync/event.rs b/pacwrap-core/src/sync/event.rs new file mode 100644 index 0000000..fa8f700 --- /dev/null +++ b/pacwrap-core/src/sync/event.rs @@ -0,0 +1,16 @@ +pub mod download; +pub mod query; +pub mod progress; + +fn whitespace(total: usize, current: usize) -> String { + let mut whitespace = String::new(); + let difference = total-current; + + if difference > 0 { + for _ in 0..difference { + whitespace.push_str(" "); + } + } + + whitespace +} diff --git a/pacwrap-core/src/sync/dl_event.rs b/pacwrap-core/src/sync/event/download.rs similarity index 96% rename from pacwrap-core/src/sync/dl_event.rs rename to pacwrap-core/src/sync/event/download.rs index 26d608b..733f5ab 100644 --- a/pacwrap-core/src/sync/dl_event.rs +++ b/pacwrap-core/src/sync/event/download.rs @@ -5,7 +5,7 @@ use dialoguer::console::Term; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use lazy_static::lazy_static; -use super::utils::whitespace; +use super::whitespace; lazy_static!{ static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}") @@ -92,7 +92,8 @@ pub fn download_event(file: &str, download: AnyDownloadEvent, this: &mut Downloa this.files_done += 1; total.set_message(format!("Total ({}{}/{})", - whitespace(this.total_files_len, this.files_done.to_string().len()), + whitespace(this.total_files_len, + this.files_done.to_string().len()), this.files_done, this.total_files)); diff --git a/pacwrap-core/src/sync/progress_event.rs b/pacwrap-core/src/sync/event/progress.rs similarity index 94% rename from pacwrap-core/src/sync/progress_event.rs rename to pacwrap-core/src/sync/event/progress.rs index 8a586c1..d906903 100644 --- a/pacwrap-core/src/sync/progress_event.rs +++ b/pacwrap-core/src/sync/event/progress.rs @@ -1,19 +1,16 @@ -use std::rc::Rc; - use alpm::Progress; use dialoguer::console::Term; use indicatif::{ProgressBar, ProgressStyle}; -use crate::constants::{BOLD, RESET}; - -use super::{utils::whitespace, transaction::{TransactionType, TransactionMode}}; +use crate::{constants::{BOLD, RESET}, sync::transaction::{TransactionType, TransactionMode}}; +use super::whitespace; #[derive(Clone)] pub struct ProgressEvent { progress: ProgressBar, offset: usize, style: ProgressStyle, - current: Rc, + current: String, } impl ProgressEvent { @@ -60,7 +57,7 @@ pub fn condensed(progress: Progress, pkgname: &str, percent: i32, howmany: usize let progress_name: String = name(progress,pkgname); let whitespace = whitespace(total.to_string().len(), pos.to_string().len()); - if this.current.as_ref() != "" { + 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()); @@ -95,7 +92,7 @@ fn name(progress: Progress, pkgname: &str) -> String { } } -fn ident(progress: Progress, pkgname: &str) -> Rc { +fn ident(progress: Progress, pkgname: &str) -> String { match progress { Progress::KeyringStart => "keyring", Progress::IntegrityStart => "integrity", diff --git a/pacwrap-core/src/sync/query_event.rs b/pacwrap-core/src/sync/event/query.rs similarity index 100% rename from pacwrap-core/src/sync/query_event.rs rename to pacwrap-core/src/sync/event/query.rs diff --git a/pacwrap-core/src/sync/filesystem.rs b/pacwrap-core/src/sync/filesystem.rs index 4b75502..d0b75e7 100644 --- a/pacwrap-core/src/sync/filesystem.rs +++ b/pacwrap-core/src/sync/filesystem.rs @@ -1,84 +1,53 @@ -use std::fmt; -use std::fs::{self, File, Metadata}; -use std::os::unix::fs::symlink; -use std::os::unix::prelude::MetadataExt; -use std::path::Path; -use std::sync::Arc; -use std::sync::mpsc::{Sender, self, Receiver}; +use std::{fs::{self, File, Metadata}, + io::Read, + os::unix::fs::symlink, + os::unix::prelude::MetadataExt, + path::Path, + sync::{Arc, mpsc::{Sender, self, Receiver}}, + collections::{HashMap, HashSet}}; use dialoguer::console::Term; -use rayon::prelude::*; -use rayon::{ThreadPool, ThreadPoolBuilder}; +use rayon::{prelude::*, {ThreadPool, ThreadPoolBuilder}}; use indexmap::IndexMap; use indicatif::{ProgressBar, ProgressStyle, ProgressDrawTarget}; -use serde::{Serialize, Deserialize, Deserializer, Serializer, de::Visitor}; +use serde::{Serialize, Deserialize}; use walkdir::WalkDir; -use std::collections::{HashMap, HashSet}; -use crate::ErrorKind; -use crate::config::{InstanceHandle, InstanceCache, InstanceType::*}; -use crate::constants::{RESET, BOLD, ARROW_CYAN, BAR_GREEN, DATA_DIR}; -use crate::utils::{print_warning, print_error}; +use crate::{ErrorKind, + config::{InstanceHandle, InstanceCache, InstanceType::*}, + constants::{RESET, BOLD, ARROW_CYAN, BAR_GREEN, DATA_DIR}, + utils::{print_warning, print_error, read_le_32}}; -impl Serialize for FileType { - fn serialize(&self, serializer: D) -> Result - where D: serde::Serializer { - serializer.serialize_i64(self.as_integer()) - } -} - - -impl <'de>Deserialize<'de> for FileType { - fn deserialize>(serializer: D) -> Result - where D: serde::Deserializer<'de> { - serializer.deserialize_i64(FileTypeVisitor) - } -} - -struct FileTypeVisitor; - -impl<'de> Visitor<'de> for FileTypeVisitor { - type Value = FileType; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "an integer between `0` and `2`") - } - - fn visit_i64(self, v: i64) -> Result - where E: serde::de::Error, { - let value = v.into(); - - if let FileType::Invalid(v) = value { - Err(E::invalid_value(serde::de::Unexpected::Signed(v), &self))? - } - - Ok(value) - } -} +static VERSION: u32 = 1; +static MAGIC_NUMBER: u32 = 408948530; #[derive(Serialize, Deserialize, Clone)] struct FileSystemState { + magic: u32, + version: u32, files: IndexMap, (FileType, Arc)> } impl FileSystemState { fn new() -> Self { Self { - files: IndexMap::new() + magic: MAGIC_NUMBER, + version: VERSION, + files: IndexMap::new(), } } } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone)] enum FileType { HardLink, SymLink, Directory, - Invalid(i64), + Invalid(i8), } -impl From for FileType { - fn from(integer: i64) -> Self { +impl From for FileType { + fn from(integer: i8) -> Self { match integer { 2 => Self::Directory, 1 => Self::SymLink, @@ -100,23 +69,11 @@ impl From for FileType { } } -impl FileType { - fn as_integer(&self) -> i64 { - match self { - Self::Directory => 2, - Self::SymLink => 1, - Self::HardLink => 0, - Self::Invalid(i) => *i, - } - } -} - enum SyncMessage { LinkComplete(Arc), SaveState(Arc, FileSystemState), } -#[allow(dead_code)] pub struct FileSystemStateSync<'a> { state_map: HashMap, FileSystemState>, state_map_prev: HashMap, FileSystemState>, @@ -228,39 +185,53 @@ impl <'a>FileSystemStateSync<'a> { return st.clone() } + let mut header_buffer = vec![0; 8]; let path = format!("{}/state/{}.dat", *DATA_DIR, instance); - let file = match File::open(&path) { + let mut file = match File::open(&path) { Ok(file) => file, - Err(err) => { - let state = FileSystemState::new(); - + Err(err) => { if err.kind() != std::io::ErrorKind::NotFound { - print_warning(format!("Reading '{}': {}", path, err.kind())); + print_error(format!("'{}': {}", path, err.kind())); } - self.state_map_prev.insert(instance.clone(), state.clone()); - return state + return self.blank_state(instance); }, }; - match ciborium::from_reader::(file) { + if let Err(error) = file.read_exact(&mut header_buffer) { + print_error(format!("'{}{instance}{}.dat': {error}", *BOLD, *RESET)); + return self.blank_state(instance); + } + + let magic = read_le_32(&header_buffer, 0); + let version = read_le_32(&header_buffer, 4); + + 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); + } + + match bincode::deserialize_from::<&File, FileSystemState>(&file) { Ok(state) => { self.state_map_prev.insert(instance.clone(), state.clone()); state }, Err(err) => { - let state = FileSystemState::new(); - - if let ciborium::de::Error::Semantic(_, error) = err { - print_error(format!("Deserialization failure occurred with '{}{instance}{}.dat': {error}", *BOLD, *RESET)); - } - - self.state_map_prev.insert(instance.clone(), state.clone()); - state + print_error(format!("Deserialization failure occurred with '{}{instance}{}.dat': {}", *BOLD, *RESET, err.as_ref())); + return self.blank_state(instance); } } } + fn blank_state(&mut self, instance: &Arc) -> FileSystemState { + let state = FileSystemState::new(); + + self.state_map_prev.insert(instance.clone(), state.clone()); + state + } + fn write(&mut self, tx: Sender<()>, ds: FileSystemState, dep: Arc) { let path: &str = &format!("{}/state/{}.dat", *DATA_DIR, dep); let output = match File::create(path) { @@ -272,7 +243,7 @@ impl <'a>FileSystemStateSync<'a> { }; self.pool().unwrap().spawn(move ||{ - if let Err(err) = ciborium::into_writer(&ds, output) { + if let Err(err) = bincode::serialize_into(output, &ds) { print_error(format!("Serialization failure occurred with '{}{dep}{}.dat': {}", *BOLD, *RESET, err.to_string())); } diff --git a/pacwrap-core/src/sync/transaction.rs b/pacwrap-core/src/sync/transaction.rs index 1587013..7042d1e 100644 --- a/pacwrap-core/src/sync/transaction.rs +++ b/pacwrap-core/src/sync/transaction.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashSet, fmt::{Display, Formatter}}; +use std::{borrow::Cow, collections::HashSet, fmt::{Display, Formatter}, fs::remove_file}; use bitflags::bitflags; use alpm::{Alpm, PackageReason, TransFlag}; @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::{config, config::InstanceHandle, - constants::{RESET, BOLD, ARROW_CYAN, BAR_CYAN, ARROW_RED}, + constants::{RESET, BOLD, ARROW_CYAN, BAR_CYAN, ARROW_RED, PACWRAP_AGENT_FILE}, sync::{ resolver_local::LocalDependencyResolver, resolver::DependencyResolver, @@ -29,8 +29,15 @@ mod stage; pub type Result = std::result::Result; +pub static MAGIC_NUMBER: u32 = 663445956; + +#[derive(Serialize, Deserialize, Clone, Debug)] pub enum Error { - AgentError, + TransactionFailureAgent, + ParameterAcquisitionFailure, + DeserializationFailure, + InvalidMagicNumber, + AgentVersionMismatch, NothingToDo, DependentContainerMissing(String), RecursionDepthExceeded(isize), @@ -104,6 +111,19 @@ pub struct TransactionMetadata<'a> { flags: (u8, u32) } +#[derive(Serialize, Deserialize)] +pub struct TransactionParameters { + magic: u32, + ver_major: u8, + ver_minor: u8, + ver_patch: u8, + bytes: u64, + files: u64, + action: TransactionType, + mode: TransactionMode, + +} + impl TransactionMode { fn bool(&self) -> bool { match self { @@ -188,6 +208,10 @@ impl Display for Error { 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::InvalidMagicNumber => write!(fmter, "Deserialization of input parameters failed: Invalid magic number."), Self::InternalError(msg) => write!(fmter, "Internal failure: {msg}"), _ => write!(fmter, "Nothing to do."), } @@ -396,14 +420,16 @@ impl <'a>TransactionHandle<'a> { fn release_on_fail(self, error: Error) { match error { - Error::AgentError => (), _ => print_error(error), + Error::TransactionFailureAgent => (), _ => print_error(error), } + remove_file(*PACWRAP_AGENT_FILE).ok(); println!("{} Transaction failed.", *ARROW_RED); drop(self); } pub fn release(self) { + remove_file(*PACWRAP_AGENT_FILE).ok(); drop(self); } @@ -439,3 +465,34 @@ impl <'a>TransactionHandle<'a> { &self.meta } } + +impl TransactionParameters { + fn new(t_type: TransactionType, t_mode: TransactionMode, dl_size: u64, amount: usize) -> Self { + Self { + magic: MAGIC_NUMBER, + ver_major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(), + ver_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(), + ver_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), + bytes: dl_size, + files: amount as u64, + action: t_type, + mode: t_mode, + } + } + + pub fn bytes(&self) -> u64 { + self.bytes + } + + pub fn files(&self) -> usize { + self.files as usize + } + + pub fn mode(&self) -> TransactionMode { + self.mode + } + + pub fn action(&self) -> TransactionType { + self.action + } +} diff --git a/pacwrap-core/src/sync/transaction/commit.rs b/pacwrap-core/src/sync/transaction/commit.rs index 5f2a780..4b081fd 100644 --- a/pacwrap-core/src/sync/transaction/commit.rs +++ b/pacwrap-core/src/sync/transaction/commit.rs @@ -1,24 +1,28 @@ -use std::process::ChildStdin; +use std::{fs::File, path::Path}; use alpm::Alpm; use dialoguer::console::Term; use serde::Serialize; -use crate::{exec::utils::execute_agent, sync::{DEFAULT_ALPM_CONF, utils::erroneous_preparation, self}}; use simplebyteunit::simplebyteunit::{SI, ToByteUnit}; -use crate::constants::{RESET, BOLD, DIM}; +use crate::{exec::utils::execute_agent, + sync::{DEFAULT_ALPM_CONF, utils::erroneous_preparation, self}, + ErrorKind, + utils::prompt::prompt, + constants::{PACWRAP_AGENT_FILE, RESET, BOLD, DIM}, + config::InstanceHandle, +}; -use crate::utils::prompt::prompt; -use crate::config::InstanceHandle; use super::{Transaction, TransactionState, TransactionHandle, TransactionAggregator, TransactionFlags, + TransactionParameters, Result, Error}; -#[allow(dead_code)] + pub struct Commit { state: TransactionState, keyring: bool, @@ -53,56 +57,72 @@ impl Transaction for Commit { erroneous_preparation(error)? } - if let Some(result) = confirm(&self.state, ag, handle) { + let result = confirm(&self.state, ag, handle); + let download = result.1.unwrap_or((0,0)); + + if let Some(result) = result.0 { return result; } - handle.set_alpm(None); - - match execute_agent(inshandle) { - Ok(mut child) => { - let stdin = child.stdin.take().unwrap(); + handle.set_alpm(None); + write_agent_params(ag, handle, download)?; + + let mut agent = match execute_agent(inshandle) { + Ok(child) => child, + Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?, + }; - write_to_stdin(&*DEFAULT_ALPM_CONF, &stdin)?; - write_to_stdin(ag.action(), &stdin)?; - write_to_stdin(handle.metadata(), &stdin)?; + match agent.wait() { + Ok(exit_status) => match exit_status.code().unwrap_or(-1) { + 0 => { + if self.keyring { + ag.keyring_update(inshandle)?; + } - match child.wait() { - Ok(exit_status) => match exit_status.code().unwrap_or(0) { - 1 => Err(Error::AgentError), - 0 => { - if self.keyring { - ag.keyring_update(inshandle)?; - } - - 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(); - state_transition(&self.state, handle, true) - }, - _ => Err(Error::TransactionFailure(format!("Generic failure of agent: Exit code {}", exit_status.code().unwrap_or(0))))?, - }, - Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?, - } + 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(); + state_transition(&self.state, handle, true) + }, + 1 => Err(Error::TransactionFailureAgent), + 2 => Err(Error::ParameterAcquisitionFailure), + 3 => Err(Error::DeserializationFailure), + 4 => Err(Error::InvalidMagicNumber), + 5 => Err(Error::AgentVersionMismatch), + _ => Err(Error::TransactionFailure(format!("Generic failure of agent: Exit code {}", exit_status.code().unwrap_or(-1))))?, }, - Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?, + Err(error) => Err(Error::TransactionFailure(format!("Execution of agent failed: {}", error)))?, } } } -fn write_to_stdin Serialize>(input: &T, stdin: &ChildStdin) -> Result<()> { - match ciborium::into_writer::(input, stdin) { +fn write_agent_params(ag: &TransactionAggregator, handle: &TransactionHandle, download: (u64, usize)) -> Result<()> { + let f = match File::create(Path::new(*PACWRAP_AGENT_FILE)) { + Ok(f) => f, + Err(error) => Err(ErrorKind::IOError((*PACWRAP_AGENT_FILE).into(), error.kind()))? + }; + + serialize(&TransactionParameters::new(*ag.action(), *handle.get_mode(), download.0, download.1), &f)?; + serialize(&*DEFAULT_ALPM_CONF, &f)?; + serialize(handle.metadata(), &f)?; + Ok(()) +} + +fn serialize Serialize>(input: &T, file: &File) -> Result<()> { + match bincode::serialize_into::<&File, T>(file, input) { Ok(()) => Ok(()), Err(error) => Err(Error::TransactionFailure(format!("Agent data serialization failed: {}", error))), } } -fn confirm(state: &TransactionState, ag: &mut TransactionAggregator, handle: &mut TransactionHandle) -> Option> { +fn confirm(state: &TransactionState, ag: &mut TransactionAggregator, handle: &mut TransactionHandle) -> (Option>, Option<(u64, usize)>) { + let mut download = None; + if ! handle.get_mode().bool() || ag.flags().intersects(TransactionFlags::DATABASE_ONLY | TransactionFlags::FORCE_DATABASE) { - summary(handle.alpm()); + download = Some(summary(handle.alpm())); if ag.flags().contains(TransactionFlags::PREVIEW) { - return Some(state_transition(state, handle, false)); + return (Some(state_transition(state, handle, false)), None); } if ! ag.flags().contains(TransactionFlags::NO_CONFIRM) { @@ -110,13 +130,13 @@ fn confirm(state: &TransactionState, ag: &mut TransactionAggregator, handle: &mu let query = format!("Proceed with {action}?"); if let Err(_) = prompt("::", format!("{}{query}{}", *BOLD, *RESET), true) { - return Some(state_transition(state, handle, false)); + return (Some(state_transition(state, handle, false)), None); } } } handle.alpm_mut().trans_release().ok(); - None + (None, download) } fn state_transition<'a>(state: &TransactionState, handle: &mut TransactionHandle, updated: bool) -> Result { @@ -129,8 +149,7 @@ fn state_transition<'a>(state: &TransactionState, handle: &mut TransactionHandle }) } -#[allow(unused_variables)] -fn summary(handle: &Alpm) { +fn summary(handle: &Alpm) -> (u64, usize) { let mut installed_size_old: i64 = 0; let mut installed_size: i64 = 0; let mut download: i64 = 0; @@ -184,8 +203,8 @@ fn summary(handle: &Alpm) { if download > 0 { println!("{}Total Download Size{}: {}", *BOLD, *RESET, download.to_byteunit(SI)); - //handle.set_dl_cb(DownloadCallback::new(download as u64, files_to_download), dl_event::download_event); } println!(); + (download as u64, files_to_download) } diff --git a/pacwrap-core/src/sync/utils.rs b/pacwrap-core/src/sync/utils.rs index 9e5d45c..d119282 100644 --- a/pacwrap-core/src/sync/utils.rs +++ b/pacwrap-core/src/sync/utils.rs @@ -60,18 +60,6 @@ impl AlpmUtils for Alpm { } } -pub fn whitespace(total: usize, current: usize) -> String { - let difference = total-current; - let mut whitespace = String::new(); - if difference > 0 { - for _ in 0..difference { - whitespace.push_str(" "); - } - } - - whitespace -} - pub fn erroneous_transaction<'a>(error: (CommitResult<'a>, alpm::Error)) -> Result<()> { match error.0 { CommitResult::FileConflict(file) => { diff --git a/pacwrap-core/src/utils.rs b/pacwrap-core/src/utils.rs index be84cad..063fb8c 100644 --- a/pacwrap-core/src/utils.rs +++ b/pacwrap-core/src/utils.rs @@ -52,7 +52,6 @@ pub fn is_color_terminal() -> bool { is_dumb && isatty(0).is_ok() && isatty(1).is_ok() } - pub fn is_truecolor_terminal() -> bool { let colorterm = match var("COLORTERM") { Ok(value) => { @@ -66,6 +65,10 @@ pub fn is_truecolor_terminal() -> bool { is_color_terminal() && colorterm } +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) +} + fn wait_on_process(mut child: Child) { child.wait().ok(); } diff --git a/pacwrap-core/src/utils/arguments.rs b/pacwrap-core/src/utils/arguments.rs index 41fad9c..95477da 100644 --- a/pacwrap-core/src/utils/arguments.rs +++ b/pacwrap-core/src/utils/arguments.rs @@ -154,5 +154,3 @@ impl Display for InvalidArgument { } } } - -